HighWaterMark voor een gewone sketch ?

algemene C code
Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

HighWaterMark voor een gewone sketch ?

Berichtdoor Koepel » 27 Dec 2016, 18:14

Bij FreeRTOS wordt de stack gevuld met een patroon, en dan kun je achteraf kijken hoeveel van de stack gebruikt is. Dat is heel handig en zou ik ook bij een gewone sketch willen hebben.
Het lukt me echter niet om het ram op een goede manier te vullen.

Deze:
http://www.avrfreaks.net/forum/soft-c-avrgcc-monitoring-stack-usage
http://arduino.stackexchange.com/questions/763/im-using-too-much-ram-how-can-this-be-measured
hebben het over .init1 tot en met .init9
Maar blijkbaar is er iets gewijzigd, want dat wordt nooit uitgevoerd.

Deze:
http://stackoverflow.com/questions/949890/how-can-i-perform-pre-main-initialization-in-c-c-with-avr-gcc
heeft het onder andere over de functie __init() die in .init1 zit, maar ook die wordt nooit uitgevoerd.

Ik krijg wel iets werkend met een functie die het ram vult. Die functie roep ik bijvoorbeeld aan tijdens het initialiseren van een globale variabele of tijdens het maken van een object via de constructor. Maar dan weet ik niet waar de ondergrens zit.

De bovengrens lukt nog wel. Ik maak een variabele op de stack, neem daar het adres van, en voor de veiligheid verlaag ik het met 16. Daaronder zit een stuk geheugen dat niet gebruikt word. Dat is zeker.

De ondergrens lukt niet. De _end is de bovenkant van de variabelen, maar ik weet niet of mijn functie die het patroon gaat schrijven de eerste is. Misschien heeft een ander object al een malloc() gedaan.

:?: Mijn vraag is: Hoe kan ik code uitvoeren voordat de objects gemaakt worden en voordat de variabelen geïnitialiseerd worden :?:
Als dat niet mogelijk is, zal ik dan de hele heap aflopen om de bovenkant van gealloceerd geheugen te vinden ? Of is dat gegarandeerd de __brkval waarde als die niet nul is ?

Dit is een kleine test, het laat zien dat Arduino heel wat variabelen en buffers maakt, en dat de stack maar klein is:
Code: Alles selecteren
uint16_t MemoryFillPattern(void);
uint16_t x = MemoryFillPattern();

void setup()
{
  Serial.begin(9600);
  while (!Serial);   // wait for serial port to connect for ATmega32U4 based boards.

  Serial.println(F( "Test HighWaterMark Stack"));
  Serial.print(F( "Filled : "));
  Serial.println( x);

  DumpRam();
}

void loop()
{
}

uint16_t MemoryFillPattern( void)
{
  extern uint8_t _end;
  extern uint8_t __stack;
 
  uint16_t count = 0;
  uint32_t *p4 = (uint32_t *) &_end;   // top of normal variables.
  byte stack_here;
  uint32_t *pEnd = (uint32_t *) &stack_here;
 
  p4 += 4;  // for safety.
  pEnd -= 16;  // for safety, try not to overwrite own stack.
 
  while( p4 <= pEnd)
  {
    *p4++ = 0xC5AA55F0;
    count += 4;
  }
  return( count);
}


void DumpRam()
{
  for( int i=0; i<=RAMEND; i++)
  {
    if( i%16 == 0)
    {
      if( i < 16)
        Serial.print( "0");
      if( i < 256)
        Serial.print( "0");
      if( i < 4096)
        Serial.print( "0");
      Serial.print( i, HEX);
      Serial.print( " ");
    }

    // Make 'i' a pointer to memory and get the contents of the that memory.
    byte data = *(byte *)i;
    Serial.print( " ");
    if( data < 16)
      Serial.print( "0");
    Serial.print( data, HEX);
    if( (i + 1) %16 == 0)
      Serial.println();
  }
  Serial.println();
}

Advertisement

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor nicoverduin » 27 Dec 2016, 18:51

Main aanpassen wordt lastig omdat er nog een stuk code voor main zelf zit. De initialisatie van het programma. Bijgaand de assembly listen van wat er allemaal gebeurt voordat jouw setup gaat lopen:
asm code
Disassembly of section .text:

00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
8: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
10: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
14: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
18: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
1c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
20: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
24: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
28: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
2c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
30: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
34: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
38: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
3c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
40: 0c 94 48 00 jmp 0x90 ; 0x90 <__vector_16>
44: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
48: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
4c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
50: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
54: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
58: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
5c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
60: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
64: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>

00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61

00000074 <__do_clear_bss>:
74: 21 e0 ldi r18, 0x01 ; 1
76: a0 e0 ldi r26, 0x00 ; 0
78: b1 e0 ldi r27, 0x01 ; 1
7a: 01 c0 rjmp .+2 ; 0x7e <.do_clear_bss_start>

0000007c <.do_clear_bss_loop>:
7c: 1d 92 st X+, r1

0000007e <.do_clear_bss_start>:
7e: a9 30 cpi r26, 0x09 ; 9
80: b2 07 cpc r27, r18
82: e1 f7 brne .-8 ; 0x7c <.do_clear_bss_loop>
84: 0e 94 92 00 call 0x124 ; 0x124 <main>
88: 0c 94 dc 00 jmp 0x1b8 ; 0x1b8 <_exit>

0000008c <__bad_interrupt>:
8c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>

00000090 <__vector_16>:
90: 1f 92 push r1
92: 0f 92 push r0
94: 0f b6 in r0, 0x3f ; 63
96: 0f 92 push r0
98: 11 24 eor r1, r1
9a: 2f 93 push r18
9c: 3f 93 push r19
9e: 8f 93 push r24
a0: 9f 93 push r25
a2: af 93 push r26
a4: bf 93 push r27
a6: 80 91 05 01 lds r24, 0x0105
aa: 90 91 06 01 lds r25, 0x0106
ae: a0 91 07 01 lds r26, 0x0107
b2: b0 91 08 01 lds r27, 0x0108
b6: 30 91 04 01 lds r19, 0x0104
ba: 23 e0 ldi r18, 0x03 ; 3
bc: 23 0f add r18, r19
be: 2d 37 cpi r18, 0x7D ; 125
c0: 20 f4 brcc .+8 ; 0xca <__vector_16+0x3a>
c2: 01 96 adiw r24, 0x01 ; 1
c4: a1 1d adc r26, r1
c6: b1 1d adc r27, r1
c8: 05 c0 rjmp .+10 ; 0xd4 <__vector_16+0x44>
ca: 26 e8 ldi r18, 0x86 ; 134
cc: 23 0f add r18, r19
ce: 02 96 adiw r24, 0x02 ; 2
d0: a1 1d adc r26, r1
d2: b1 1d adc r27, r1
d4: 20 93 04 01 sts 0x0104, r18
d8: 80 93 05 01 sts 0x0105, r24
dc: 90 93 06 01 sts 0x0106, r25
e0: a0 93 07 01 sts 0x0107, r26
e4: b0 93 08 01 sts 0x0108, r27
e8: 80 91 00 01 lds r24, 0x0100
ec: 90 91 01 01 lds r25, 0x0101
f0: a0 91 02 01 lds r26, 0x0102
f4: b0 91 03 01 lds r27, 0x0103
f8: 01 96 adiw r24, 0x01 ; 1
fa: a1 1d adc r26, r1
fc: b1 1d adc r27, r1
fe: 80 93 00 01 sts 0x0100, r24
102: 90 93 01 01 sts 0x0101, r25
106: a0 93 02 01 sts 0x0102, r26
10a: b0 93 03 01 sts 0x0103, r27
10e: bf 91 pop r27
110: af 91 pop r26
112: 9f 91 pop r25
114: 8f 91 pop r24
116: 3f 91 pop r19
118: 2f 91 pop r18
11a: 0f 90 pop r0
11c: 0f be out 0x3f, r0 ; 63
11e: 0f 90 pop r0
120: 1f 90 pop r1
122: 18 95 reti

00000124 <main>:
124: 78 94 sei
126: 84 b5 in r24, 0x24 ; 36
128: 82 60 ori r24, 0x02 ; 2
12a: 84 bd out 0x24, r24 ; 36
12c: 84 b5 in r24, 0x24 ; 36
12e: 81 60 ori r24, 0x01 ; 1
130: 84 bd out 0x24, r24 ; 36
132: 85 b5 in r24, 0x25 ; 37
134: 82 60 ori r24, 0x02 ; 2
136: 85 bd out 0x25, r24 ; 37
138: 85 b5 in r24, 0x25 ; 37
13a: 81 60 ori r24, 0x01 ; 1
13c: 85 bd out 0x25, r24 ; 37
13e: 80 91 6e 00 lds r24, 0x006E
142: 81 60 ori r24, 0x01 ; 1
144: 80 93 6e 00 sts 0x006E, r24
148: 10 92 81 00 sts 0x0081, r1
14c: 80 91 81 00 lds r24, 0x0081
150: 82 60 ori r24, 0x02 ; 2
152: 80 93 81 00 sts 0x0081, r24
156: 80 91 81 00 lds r24, 0x0081
15a: 81 60 ori r24, 0x01 ; 1
15c: 80 93 81 00 sts 0x0081, r24
160: 80 91 80 00 lds r24, 0x0080
164: 81 60 ori r24, 0x01 ; 1
166: 80 93 80 00 sts 0x0080, r24
16a: 80 91 b1 00 lds r24, 0x00B1
16e: 84 60 ori r24, 0x04 ; 4
170: 80 93 b1 00 sts 0x00B1, r24
174: 80 91 b0 00 lds r24, 0x00B0
178: 81 60 ori r24, 0x01 ; 1
17a: 80 93 b0 00 sts 0x00B0, r24
17e: 80 91 7a 00 lds r24, 0x007A
182: 84 60 ori r24, 0x04 ; 4
184: 80 93 7a 00 sts 0x007A, r24
188: 80 91 7a 00 lds r24, 0x007A
18c: 82 60 ori r24, 0x02 ; 2
18e: 80 93 7a 00 sts 0x007A, r24
192: 80 91 7a 00 lds r24, 0x007A
196: 81 60 ori r24, 0x01 ; 1
198: 80 93 7a 00 sts 0x007A, r24
19c: 80 91 7a 00 lds r24, 0x007A
1a0: 80 68 ori r24, 0x80 ; 128
1a2: 80 93 7a 00 sts 0x007A, r24
1a6: 10 92 c1 00 sts 0x00C1, r1
1aa: c0 e0 ldi r28, 0x00 ; 0
1ac: d0 e0 ldi r29, 0x00 ; 0
1ae: 20 97 sbiw r28, 0x00 ; 0
1b0: f1 f3 breq .-4 ; 0x1ae <main+0x8a>
1b2: 0e 94 00 00 call 0 ; 0x0 <__vectors>
1b6: fb cf rjmp .-10 ; 0x1ae <main+0x8a>

000001b8 <_exit>:
1b8: f8 94 cli

000001ba <__stop_program>:
1ba: ff cf rjmp .-2 ; 0x1ba <__stop_program>

Disassembly of section .bss:

00800100 <__bss_start>:
800100: 00 00 nop
...

00800104 <timer0_fract>:
...

00800105 <timer0_millis>:
800105: 00 00 00 00 ....

De .bss (het segment waar je variabelen komen wordt geinitialiseerd. Alle vectoren worden gezet die van belang zijn. Dit is overigens een lege sketch dus zie je setup() en loop() nog niet omdat daar niets gebeurt en gooit de linker ze er gelijk uit.

Verder heb je nog een zooi registers ter beschikking

asm code
SYMBOL TABLE:
00800100 l d .data 00000000 .data
00000000 l d .text 00000000 .text
00800100 l d .bss 00000000 .bss
00800105 l O .bss 00000004 timer0_millis
00800104 l O .bss 00000001 timer0_fract
00800100 l O .bss 00000004 timer0_overflow_count
0000007e l .text 00000000 .do_clear_bss_start
0000007c l .text 00000000 .do_clear_bss_loop
000001ba l .text 00000000 __stop_program
0000008c w .text 00000000 __vector_22
0000008c w .text 00000000 __vector_1
00000068 g .text 00000000 __trampolines_start
000001bc g .text 00000000 _etext
0000008c w .text 00000000 __vector_24
0000008c w .text 00000000 __vector_12
0000008c g .text 00000000 __bad_interrupt
0000008c w .text 00000000 __vector_6
00000068 g .text 00000000 __trampolines_end
0000008c w .text 00000000 __vector_3
0000008c w .text 00000000 __vector_23
00000068 g .text 00000000 __dtors_end
00800109 g .bss 00000000 __bss_end
0000008c w .text 00000000 __vector_25
0000008c w .text 00000000 __vector_11
00000068 w .text 00000000 __init
0000008c w .text 00000000 __vector_13
0000008c w .text 00000000 __vector_17
0000008c w .text 00000000 __vector_19
0000008c w .text 00000000 __vector_7
00000074 g .text 00000010 .hidden __do_clear_bss
00000000 g .text 00000000 __vectors
00000000 w .text 00000000 __vector_default
0000008c w .text 00000000 __vector_5
00000068 g .text 00000000 __ctors_start
00800100 g .bss 00000000 __bss_start
00000124 g F .text 00000094 main
0000008c w .text 00000000 __vector_4
0000008c w .text 00000000 __vector_9
0000008c w .text 00000000 __vector_2
0000008c w .text 00000000 __vector_21
0000008c w .text 00000000 __vector_15
00000068 g .text 00000000 __dtors_start
00000068 g .text 00000000 __ctors_end
00800100 g .data 00000000 _edata
0000008c w .text 00000000 __vector_8
000001b8 w .text 00000000 .hidden exit
000001b8 g .text 00000000 .hidden _exit
0000008c w .text 00000000 __vector_14
0000008c w .text 00000000 __vector_10
00000090 g F .text 00000094 __vector_16
0000008c w .text 00000000 __vector_18
0000008c w .text 00000000 __vector_20

.data is het stuk van adres 0x00 - 0xFF daar zet het systeem een dingen en zitten vermoedelijk ook de adressen van o.a. IO poorten. Maar dat zou je moeten uitzoeken. Je eigen data wordt vanaf adres 0x100 gevuld. Eerst krijg je de globale variabelen. Als deze geinitialiseerd zijn (char arrays ed) dan worden die eerst gevuld vanuit het Flash geheugen. Daarna komt de rest.
Dan begint de Heap en wordt de SP gezet op de top van het RAM geheugen. De stack werkt dus terug in de Heap. Per saldo zou dat theoretisch samen kunnen komen.
Al deze info kun je gewoon uit de archive halen via objdump. Staat ook ergens in de IDE compiler.

Overigens er zijn wel verschillende programma's die het vrije geheugen laten zien. Die kloppen. Zelf had ik ooit ook eea gedaan. Maar kan de sketch ff niet vinden waar ik met name naar dynamische allocatie ben gaan kijken en of je niet een soort RAM defragmenter kan bouwen. Zou theoretisch moeten kunnen... Maar vereist nog wel eea aan denkwerk.

@edit: Gevonden. Een sketch waar ik nogal wat zat te pielen met dynamisch alloceren van het geheugen en gelijk een dump functie om eea te onderzoeken. Kijk maar of je er wat aan hebt.

cpp code
#include "Arduino.h"
#include "MemoryFree.h"
//The setup function is called once at startup of the sketch

uint8_t * p1;
uint8_t * p2;
uint8_t * p3;
uint8_t * p4;
uint8_t * p5;
uint8_t * p6;
uint8_t * p7;
uint8_t *copyP1;

struct __freelist {
size_t sz;
struct __freelist *nx;
};

void setup() {

Serial.begin(9600);

char buffer[70];

sprintf(buffer, "FreeMemory = %d", freeMemory());
Serial.println(buffer);

p1 = (uint8_t *)malloc(10 * sizeof(uint8_t));
p2 = (uint8_t *)malloc(15 * sizeof(uint8_t));
p3 = (uint8_t *)malloc(20 * sizeof(uint8_t));
p4 = (uint8_t *)malloc(25 * sizeof(uint8_t));
p5 = (uint8_t *)malloc(30 * sizeof(uint8_t));
p6 = (uint8_t *)malloc(35 * sizeof(uint8_t));
p7 = (uint8_t *)malloc(40 * sizeof(uint8_t));

copyP1 = p1;

for (uint8_t i = 0; i < 10; i++) {
p1[i] = 1;
}
for (uint8_t i = 0; i < 15; i++) {
p2[i] = 2;
}
for (uint8_t i = 0; i < 20; i++) {
p3[i] = 3;
}
for (uint8_t i = 0; i < 25; i++) {
p4[i] = 4;
}
for (uint8_t i = 0; i < 30; i++) {
p5[i] = 5;
}
for (uint8_t i = 0; i < 35; i++) {
p6[i] = 6;
}
for (uint8_t i = 0; i < 40; i++) {
p7[i] = 7;
}

free(p2);
free(p4);
free(p6);

printHeapList();

}

// The loop function is called in an endless loop
void loop() {
}

/**
* print heap list
*/
void printHeapList() {
char buffer[100];
//
// show generic info
//
sprintf(buffer, "heap_start = %04X\n", (uint16_t)__malloc_heap_start);
Serial.print(buffer);
uint16_t *freeRamPtr = (uint16_t *)(__malloc_heap_start - 4);
sprintf(buffer, "unused RAM = %04X\n", (uint16_t)freeRamPtr[0]);
Serial.print(buffer);

// printDump((uint8_t *)((uint16_t)__malloc_heap_start & 0xFFF0), (uint8_t *)*freeRamPtr);
// printDump((uint8_t*) 0x100, (uint8_t *)*freeRamPtr);
//
// Get first location
//
uint16_t *freePtr = (uint16_t*)__malloc_heap_start - 2;
if (freePtr[1] != 0) {
//
// walk the free segments until the next ptr = 0
//
do {
//
// point to next location
//
freePtr = (uint16_t *)freePtr[1];
//
// get size of each free segment
//
uint16_t freeSize = freePtr[0];
//
// display output
//
sprintf(buffer, "location : %04X has %d bytes free\n", (uint16_t)freePtr, freeSize + 2);
Serial.print(buffer);
} while (freePtr[1] != 0);
}
// printDump((uint8_t*) 0x100, (uint8_t *)*freeRamPtr);

//
// print used allocated variables
//
freePtr = (uint16_t*)__malloc_heap_start - 2;
uint16_t *varPtr = (uint16_t *)__malloc_heap_start;
//
// used variable
//
if ((uint16_t)varPtr < freePtr[0]) {
sprintf(buffer, "Used variable at %04X with length %d bytes\n", (uint16_t)varPtr+2, (uint16_t)varPtr[0]);
Serial.print(buffer);
}
//
// search for variable
//
for (uint16_t i = 0x100; i < (uint16_t)__malloc_heap_start; i++) {
// Serial.println((uint16_t)*i, HEX);
// Serial.println(*(uint16_t *)i, HEX);
if (*(uint16_t *)i == (uint16_t)varPtr+2 ) {
//
// found pointer
//
sprintf(buffer, "Used variable itself at %04X\n", i);
Serial.print(buffer);

}
}


}
void printDump (uint8_t *beginAddress, uint8_t *endAddress) {
char buffer[70];
//
// print memory dump
//
uint8_t *ptr = beginAddress;
//
// we will always finish the line
//
while ((uint16_t)ptr < (uint16_t)endAddress) {
sprintf(buffer, "\n%04X ", (uint16_t)ptr);
Serial.print(buffer);
for (uint8_t i = 0; i < 16; i++) {
sprintf(buffer, "%02X ", *ptr);
Serial.print(buffer);
ptr++;
}
//
// print character representation of dump values
//
ptr = ptr - 16; // jump back to the start of this line

for (uint8_t i = 0; i < 16; i++) {
// if (*ptr == 0x0D || *ptr == 0x0A) {
if (*ptr < 0x0F ) {
Serial.print(".");
} else {
Serial.print((char)*ptr);
}
ptr++;
}
}
Serial.println();
}

void createString(){
char buffer[70];
uint8_t *ptr;
ptr = (uint8_t *)malloc (20* sizeof(uint8_t));
Serial.print("ptr points to = ");
Serial.println((uint16_t)ptr, HEX);
sprintf(buffer, "FreeMemory = %d", freeMemory());
Serial.println(buffer);
printHeapList();
}
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor Koepel » 27 Dec 2016, 19:23

Bedankt, ik ga jouw heap sketch eens proberen om het effect op __brkval te zien.
De avr-objdump had ik al gebruikt, maar ik ben niet goed in AVR assembly.

Het gaat me niet om de hoeveelheid vrij geheugen op een bepaald moment, maar om te zien hoeveel geheugen er ooit maximaal is gebruikt. Dat wil ik gaan toevoegen aan mijn Arduino Mega + Ethernet Shield. Het kan 4 kbyte zijn en het kan 4 byte zijn, ik heb geen idee.

Als patroon gebruiken anderen slechts één byte, maar dan krijg ik false positives. Dus ik ga vier verschillende bytes gebruiken, of ik ga kijken of dat ene byte vier keer achter elkaar voorkomt.

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor Koepel » 28 Dec 2016, 09:08

Het lukt me niet :evil:

Wanneer ik het geheugen scan op het patroon, dan krijg ik de eerste keer een heel laag nummer terug. Zodra ik echter een pointer afdruk of het geheugen dump, dan gaat het wel goed. Het lijkt wel een kwantumdeeltje, zodra je er naar kijkt dan verandert het.
Het zal te maken hebben met compiler-optimalisatie, maar een #pragma of een __attribute__ om het wel te laten werken heb ik nog niet gevonden.

De pointer 'p' kan ik 'static' of 'volatile' maken, dat maakt geen verschil.
Het is alleen op een Arduino Uno. Als ik het probeer op een Arduino Micro dan onstaat die bug niet.

De __brkval gebruiken gaat goed. Dat is de bovenkant van de heap, ongeacht hoe gefragmenteerd de heap daaronder is.

Dit is voorlopig de code.
Code: Alles selecteren
// -----------------------------------------
// HighWaterMark of the stack
// -----------------------------------------
// Calculate the high water mark for a Arduino with AVR microcontroller.
// It is the amount of memory that was never used since the Arduino was started.
//
// The __init() or .init1 up to .init9 was used in the past.
// It was used to run assembly code to fill the unused ram with a pattern.
// With Arduino 1.8.0 that did no longer work.
//
// As an alternative, a function is used that fills ram with a pattern
// between the top of the heap and the bottom of the stack.
// The functions to fill and read the patterns are not accurate to the last byte.
//
//
// First version, December 2016, by Koepel, Public Domain.
//    Tested with Arduino Uno and Arduino IDE 1.8.0
//    Not fully working yet. There is a bug that makes the
//    number of found unused ram a low number (for example 10)
//    when MemoryNotUsed() is called for the first time.
//


// The _end address is the same as the __heap_start address.
// The __stack address is the same as RAMEND;
// The __brkval is zero when the heap is not used yet,
// otherwise it is the top of the heap.

extern uint8_t __heap_start;
extern uint8_t *__brkval;

const byte memory_pattern = 0xC5;


void setup()
{
  // Fill the unused memory with a pattern.
  // This function could be called before the setup() function,
  // with a global variable that is filled with the return value
  // of the function. In that case, function prototyping is needed.
  int fill_size = MemoryFillPattern();

 
  Serial.begin(9600);
  while (!Serial);   // wait for serial port to connect for ATmega32U4 based boards.

  Serial.println(F( "HighWaterMark test sketch"));
  Serial.print(F( "Filled "));
  Serial.print( fill_size);
  Serial.println(F( " bytes with a pattern"));
}


void loop()
{
  Serial.print(F( "MemoryNotUsed is "));
  Serial.print( MemoryNotUsed());
  Serial.println(F( " bytes"));

  delay(5000);
}


int MemoryNotUsed( void)
{
  byte * pBottom = &__heap_start;
  int count = 0;

  // When the heap is in use, the __brkval is no longer zero,
  // and the value is set to above the used part of the heap.
  if( __brkval != 0)
    pBottom = (byte *) __brkval;
   
  byte *p = (byte *) &count;  // use the address of a variable on the stack.

  // The heap could have been fragmented, and allocated memory could
  // have been filled with zero or just left as it is.
  // It is hard to start searching from the bottom up.
  // Therefor the pattern is searched from the current stack location downwards.
  // To be sure there must be four consecutive bytes with the pattern.

  // ------------------------------
  // Bug !   Code not fully working yet.
  // ------------------------------
  // When the MememoryNotUsed() is called for the first time, it
  // returns a low number, for example 10. That seems to have to do
  // with compiler optimizations, since it does not happen as soon
  // as I print a pointer or dump the memory.
  // As a fix, the pointer can be lowered at least 44 bytes.
  // For a Arduino Micro this fix is not needed.
  // For a Arduino Uno, this fix can be used to make it work the first time.
  //  p -= 44; // Fix. These 44 bytes are needed to make it work


  // Search memory downwards until the pattern is found.
  while( !(p[0] == memory_pattern &&
    p[1] == memory_pattern &&
    p[2] == memory_pattern &&
    p[3] == memory_pattern) &&
    p > pBottom)
  {
    p--;   
  }

  p += 3;      // the pattern was found at p[0] up to p[3]. Start with p[3].

  // The pattern was found.
  // Check how much memory is still filled with the pattern
  while( *p == memory_pattern && p > pBottom)
  {
    p--;
    count++;
  }

  return( count);
}


int MemoryFillPattern( void)
{
  byte *p = &__heap_start;
  int count = 0;

  // When the heap is in use, then the __brkval is no longer zero,
  // and the value is set to above the used part of the heap.
  if( __brkval != 0)
    p = (byte *) __brkval;

  byte *pTop = (byte *) &count;  // use the address of a variable on the stack.
 
  p += 1;  // I don't know if __brkval is the last used, or the first free byte.
  pTop -= 16;   // Be sure not to overwrite own stack variables.

  while( p < pTop)
  {
    *p++ = memory_pattern;
    count++;
  }
  return( count);
}



// ----------------------------
// DumpRam()
// ----------------------------
// Function used during development to dump the whole ram.
// Warning: The actual ram data starts at 0x100.
//
void DumpRam()
{
  for( int i=0; i<=RAMEND; i++)
  {
    if( i%16 == 0)
    {
      if( i < 16)
        Serial.print( "0");
      if( i < 256)
        Serial.print( "0");
      if( i < 4096)
        Serial.print( "0");
      Serial.print( i, HEX);
      Serial.print( " ");
    }

    // Make 'i' a pointer to memory and get the contents of the that memory.
    byte data = *(byte *)i;
    Serial.print( " ");
    if( data < 16)
      Serial.print( "0");
    Serial.print( data, HEX);
    if( (i + 1) %16 == 0)
      Serial.println();
  }
  Serial.println();
}

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor nicoverduin » 28 Dec 2016, 09:51

Dit klopt sowieso niet:
cpp code
extern uint8_t __heap_start;

De __heap_start is een 16 bits pointer naar de eerste vrije byte. Nu zeg je dat het een 8 bits waarde is.
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor Koepel » 28 Dec 2016, 11:18

Dat vroeg ik me dus af hoe ik dat het beste kan doen.
Het is een label, meer niet. Dus op 'c++' niveau wordt het adres er van gebruikt als een void *
Bij de Arduino Uno is de pointer zelf 16 bits, maar of dat nu 16 of 32 bits zijn, dat maakt niet uit voor de sketch.
Maar het label zelf is geen echte variabele. Ik heb dus alleen het adres er van nodig met &__heap_start
Ik zie het meer als een label, net zoals het adres van een functie.

Dus op een 8-bit AVR chip is het meest logische om net te doen alsof het wijst naar een byte, maar het wijst meer naar een void achtig iets, omdat het een linker label is.

Ik ben niet de enige die een byte of char er voor gebruikt. Sommigen gebruiken een int, dat kan ook, aangezien een int ook op een oneven adres kan staan bij de 8-bit AVR chips.

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor nicoverduin » 28 Dec 2016, 12:36

Ik zou het zelf anders doen maar je redenering gaat wel op
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor nicoverduin » 28 Dec 2016, 17:41

Ik heb ff een sketch gemaakt om een zooi van die variabelen te printen uit de .data en .bss segmenten als ook de stack. kijk maar of je er wat aan hebt.
cpp code
#include <Arduino.h>

//
// variabelen uit de dump
//
extern uint16_t __brkval;
extern uint32_t timer0_overflow_count;
extern uint32_t timer0_millis;
extern char *__malloc_heap_start;
extern uint16_t __bss_end;
extern char *__malloc_heap_end;
extern uint16_t __data_end;
extern uint16_t __bss_start;
extern uint8_t *_edata;
extern uint8_t *__flp;
extern uint16_t __data_start;
extern uint16_t __malloc_margin;



/**
* setup()
*/
void setup()
{
Serial.begin(115200);
printSystemVariables();
}


void loop()
{
}
/**
* @fn printDump
* @param beginAddress adres waar te starten
* @param endAddress adres waar te eindigen (worden altijd 16 bytes)
* print een ouderwetse MSDos dump van het geheugen
*/
void printDump (uint8_t *beginAddress, uint8_t *endAddress) {
char buffer[70];
//
// print memory dump
//
uint8_t *ptr = beginAddress;
//
// we will always finish the line
//
while ((uint16_t)ptr < (uint16_t)endAddress) {
sprintf(buffer, "\n%04X ", (uint16_t)ptr);
Serial.print(buffer);
for (uint8_t i = 0; i < 16; i++) {
sprintf(buffer, "%02X ", *ptr);
Serial.print(buffer);
ptr++;
}
//
// print character representation of dump values
//
ptr = ptr - 16; // jump back to the start of this line

for (uint8_t i = 0; i < 16; i++) {
// if (*ptr == 0x0D || *ptr == 0x0A) {
if (*ptr < 0x0F ) {
Serial.print(".");
} else {
Serial.print((char)*ptr);
}
ptr++;
}
}
Serial.println();
Serial.println();
}
/**
* @fn printSystemVariables()
* Prints systems variables of Memory RAM usage
*/
void printSystemVariables(){

char printBuffer[100];

sprintf(printBuffer,"__malloc_heap_start.. : %04X = %04X\n", (uint16_t)&__malloc_heap_start, (uint16_t)*&__malloc_heap_start); Serial.print(printBuffer);
sprintf(printBuffer,"__malloc_heap_end.... : %04X = %04X\n", (uint16_t)&__malloc_heap_end, (uint16_t)*&__malloc_heap_end); Serial.print(printBuffer);
sprintf(printBuffer,"__malloc_margin...... : %04X = %04X\n", (uint16_t)&__malloc_margin, (uint16_t)*&__malloc_margin); Serial.print(printBuffer);
printDump((uint8_t *)__malloc_heap_start, (uint8_t *)__malloc_heap_start + 0x10);
sprintf(printBuffer,"__data_start......... : %04X\n", (uint16_t)&__data_start); Serial.print(printBuffer);
sprintf(printBuffer,"__data_end........... : %04X\n", (uint16_t)&__data_end); Serial.print(printBuffer);
printDump((uint8_t *)&__data_start, (uint8_t *)&__data_end);
sprintf(printBuffer,"__brkval............. : %04X = %04X\n\n", (uint16_t)&__brkval, (uint16_t)*&__brkval); Serial.print(printBuffer);
sprintf(printBuffer,"__bss_start.......... : %04X\n", (uint16_t)&__bss_start); Serial.print(printBuffer);
sprintf(printBuffer,"__bss_end............ : %04X\n", (uint16_t)&__bss_end); Serial.print(printBuffer);
printDump((uint8_t *)&__bss_start, (uint8_t *)&__bss_end);
sprintf(printBuffer,"_edata............... : %04X = %04X\n", (uint16_t)&_edata, (uint16_t)&*_edata); Serial.print(printBuffer);
sprintf(printBuffer,"__flp................ : %04X = %04X\n", (uint16_t)&__flp, (uint16_t)&*__flp); Serial.print(printBuffer);
sprintf(printBuffer,"timer0_overflow_count : %08X\n", timer0_overflow_count); Serial.print(printBuffer);
sprintf(printBuffer,"timer0_millis........ : %08X\n", timer0_millis); Serial.print(printBuffer);
sprintf(printBuffer,"Stack Pointer........ : %04X\n", (uint16_t)SP); Serial.print(printBuffer);
sprintf(printBuffer,"RAM End.............. : %04X\n", (uint16_t)RAMEND); Serial.print(printBuffer);
memset(printBuffer, 0, sizeof(printBuffer));
uint16_t sp = SP;
sprintf(printBuffer,"Stack usage.......... : %d bytes\n", (uint16_t)(RAMEND - sp)); Serial.print(printBuffer);
printDump((uint8_t *)(sp & 0xFFF0), (uint8_t *)RAMEND);
}


Output van dit zelfde programmatje

cpp code
__malloc_heap_start.. : 0102 = 03C4
__malloc_heap_end.... : 0100 = 0000
__malloc_margin...... : 0104 = 0080

03C4 00 00 30 30 30 30 30 30 30 0A 74 69 6D 65 72 30 ..0000000.timer0

__data_start......... : 0100
__data_end........... : 031A

0100 00 00 C4 03 80 00 00 00 00 00 E3 00 5F 00 AC 00 ..Ä.€.....ã._.¬.
0110 8A 00 9E 00 2A 01 0D 0A 00 0A 25 30 34 58 20 00 Š.ž.*.....%04X .
0120 25 30 32 58 20 00 2E 00 5F 5F 6D 61 6C 6C 6F 63 %02X ...__malloc
0130 5F 68 65 61 70 5F 73 74 61 72 74 2E 2E 20 3A 20 _heap_start.. :
0140 25 30 34 58 20 3D 20 25 30 34 58 0A 00 5F 5F 6D %04X = %04X..__m
0150 61 6C 6C 6F 63 5F 68 65 61 70 5F 65 6E 64 2E 2E alloc_heap_end..
0160 2E 2E 20 3A 20 25 30 34 58 20 3D 20 25 30 34 58 .. : %04X = %04X
0170 0A 00 5F 5F 6D 61 6C 6C 6F 63 5F 6D 61 72 67 69 ..__malloc_margi
0180 6E 2E 2E 2E 2E 2E 2E 20 3A 20 25 30 34 58 20 3D n...... : %04X =
0190 20 25 30 34 58 0A 00 5F 5F 64 61 74 61 5F 73 74 %04X..__data_st
01A0 61 72 74 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 art......... : %
01B0 30 34 58 0A 00 5F 5F 64 61 74 61 5F 65 6E 64 2E 04X..__data_end.
01C0 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 30 34 .......... : %04
01D0 58 0A 00 5F 5F 62 72 6B 76 61 6C 2E 2E 2E 2E 2E X..__brkval.....
01E0 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 30 34 58 20 ........ : %04X
01F0 3D 20 25 30 34 58 0A 0A 00 5F 5F 62 73 73 5F 73 = %04X...__bss_s
0200 74 61 72 74 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A tart.......... :
0210 20 25 30 34 58 0A 00 5F 5F 62 73 73 5F 65 6E 64 %04X..__bss_end
0220 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 ............ : %
0230 30 34 58 0A 00 5F 65 64 61 74 61 2E 2E 2E 2E 2E 04X.._edata.....
0240 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 30 34 .......... : %04
0250 58 20 3D 20 25 30 34 58 0A 00 5F 5F 66 6C 70 2E X = %04X..__flp.
0260 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 ...............
0270 3A 20 25 30 34 58 20 3D 20 25 30 34 58 0A 00 74 : %04X = %04X..t
0280 69 6D 65 72 30 5F 6F 76 65 72 66 6C 6F 77 5F 63 imer0_overflow_c
0290 6F 75 6E 74 20 3A 20 25 30 38 58 0A 00 74 69 6D ount : %08X..tim
02A0 65 72 30 5F 6D 69 6C 6C 69 73 2E 2E 2E 2E 2E 2E er0_millis......
02B0 2E 2E 20 3A 20 25 30 38 58 0A 00 53 74 61 63 6B .. : %08X..Stack
02C0 20 50 6F 69 6E 74 65 72 2E 2E 2E 2E 2E 2E 2E 2E Pointer........
02D0 20 3A 20 25 30 34 58 0A 00 52 41 4D 20 45 6E 64 : %04X..RAM End
02E0 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A .............. :
02F0 20 25 30 34 58 0A 00 53 74 61 63 6B 20 75 73 61 %04X..Stack usa
0300 67 65 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 25 ge.......... : %
0310 64 20 62 79 74 65 73 0A 00 00 D1 00 00 00 05 D7 d bytes...Ó....Ù

__brkval............. : 03C0 = 0000

__bss_start.......... : 031A
__bss_end............ : 03C4

031A DD 00 00 00 29 E3 00 00 00 0A 01 00 00 E8 03 00 á...2æ.......è..
032A 00 00 00 00 00 C5 00 C4 00 C0 00 C1 00 C2 00 C6 .....Å.Ä.À.Á.Â.Æ
033A 00 01 00 00 3B 3F 00 00 00 00 00 00 00 00 00 00 ....#%..........
034A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
035A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
036A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
037A 00 00 00 00 00 00 2E 0A 30 33 37 41 20 30 30 20 ........037A 00
038A 38 41 20 33 38 20 34 31 20 32 30 20 33 33 20 33 8A 38 41 20 33 3
039A 38 20 32 30 20 32 30 20 33 32 20 33 30 20 32 30 2 30 20 32 30 20
03AA 20 33 33 20 33 32 20 32 30 20 32 30 20 33 32 20 20 32 30 20 32
03BA 33 30 20 32 30 20 00 00 00 00 00 00 30 30 30 30 00 00 ......0000

_edata............... : 031A = 011C
__flp................ : 03C2 = 0000
timer0_overflow_count : 00000122
timer0_millis........ : 0000012C
Stack Pointer........ : 0887
RAM End.............. : 08FF
Stack usage.......... : 132 bytes

0870 08 97 08 02 5D 00 E3 03 24 08 09 08 0A 00 22 00 .—..].ã.$.....".
0880 22 63 E3 00 00 FE 08 97 08 98 08 7B 00 00 03 05 "cã..þ.—.˜.{....
0890 11 01 03 D4 03 D0 04 6E 53 74 61 63 6B 20 75 73 ..Ô.Ð.nStack us
08A0 61 67 65 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 20 3A 20 age.......... :
08B0 31 33 32 20 62 79 74 65 73 0A 00 00 00 00 00 00 132 bytes.......
08C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08F0 00 00 00 00 00 00 00 00 00 00 00 00 00 34 00 5B .............4.[
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor nicoverduin » 28 Dec 2016, 17:56

ook de moeite waard om te bekijken : http://andybrown.me.uk/2011/01/01/debug ... llocation/
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: HighWaterMark voor een gewone sketch ?

Berichtdoor Koepel » 29 Dec 2016, 09:51

Bedankt Nico. Ik heb het goed bekeken. Het meeste gaat over de heap, maar ik wil niet het totale ongebruikte geheugen berekenen op een bepaald moment, maar de HighWaterMark. Dat is dus het stukje geheugen tussen de stack en de heap dat nog nooit gebruikt is.
Dan kan ik (nadat het een paar dagen heeft gelopen) serieus inschatten hoeveel data buffers ik nog zou kunnen declareren.

Ik wist niet dat 'SP' de combinatie van de SPL en SPH registers is. Dat maakt het opvragen van de stackpointer wel heel eenvoudig :D
Sommigen gebruiken 'AVR_STACK_POINTER_REG', dat is via een #define in common.h hetzelfde als 'SP'.

Helaas krijg ik de bug er niet uit, dat voor een Arduino Uno ik de eerste keer slechts een waarde van 10 byte krijg als HighWaterMark, en pas de tweede keer de juiste waarde van zo'n 1769. Zodra ik variabelen of geheugen dump, dan verdwijnt de bug. Dus mijn sketch is niet af, en ik zet het niet op Github/Gist.

Volgende

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 12 gasten