Go Down

Topic: modify freeRam() to find fragmentation (Read 10358 times) previous topic - next topic

Thomas499

Quote
Which address do you now want to print? That of the pointer or that of what it points to?
Both preferably. But I figured out how to print what it points to, I need to figure out how to print the pointer.

Quote
int thisVariableHasAnAddress;
Serial.print(&thisVariableHasAnAddress);
I tried that before I asked on here. The youtube examples do it this way too. But I get this error message
Quote
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Print.h:66:12: note: size_t Print::print(long unsigned int, int) <near match>

     size_t print(unsigned long, int = DEC);

            ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Print.h:66:12: note:   no known conversion for argument 1 from 'int*' to 'long unsigned int'

exit status 1
call of overloaded 'print(int*)' is ambiguous

RayLivingston

Not quite.

An int must support the range -32767..32767, which effectively means it must be at least 16 bits wide on a binary computer. Similarly, a long must support the range -2147483647..2147483647, or at least 32 bits wide. (Those ranges easily allow for one's complement and sign/magnitude systems.)

Also, there are constraints on the relative sizes of types: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) <= sizeof (long long).
Not so fast...  There are as many c specifications as Carter has little liver pills.  You're talking about some of the many later c specifications (C99, ANSI, etc.), which differ greatly even from each other.  Original K&R c contained only a few simple types, with no hard and fast definitions of their sizes.  In fact, IIRC, it ran on a DEC PDP-8, which was a 12-bit machine, so would not likely have used a 16-bit data type for much of anything.  

From Kernighans "The C Programming Language":

Quote
2.2 Data Types and Sizes

There are only a few basic data types in C:
    char a single byte, capable of holding one character in the local character set
    int an integer, typically reflecting the natural size of integers on the host machine
    float single-precision floating point
    double double-precision floating point
Remember, c was first implemented on a DEC PDP-8, which has a
Regards,
Ray L.

christop

You're talking about some of the many later c specifications (C99, ANSI, etc.), which differ greatly even from each other.
All of the standards (C89, C99, C11) are virtually identical in the size limits of the basic types.

Quote
In fact, IIRC, it ran on a DEC PDP-8, which was a 12-bit machine, so would not likely have used a 16-bit data type for much of anything.
It was born on the PDP-11, a 16-bit machine.

The PDP-8 is not very well suited to C--for one, it has no stack--but a C compiler for the PDP-8 would likely have a 12-bit char, 24-bit short and int, and either a 36-bit or 48-bit long. Or it might have a 36-bit short, int, and long. It depends on the implementer.

PaulS

Both preferably. But I figured out how to print what it points to, I need to figure out how to print the pointer.
 I tried that before I asked on here. The youtube examples do it this way too. But I get this error message
Code: [Select]
void setup()
{
  Serial.begin(115200);
  int a = 45;
  Serial.println((unsigned long)&a);
}

void loop()
{
}

Compiles, at least.
The art of getting good answers lies in asking good questions.

Thomas499

It does complie, but it doesn't run correctly.
Code: [Select]
void setup()
{
  Serial.begin(115200);
  int b = 77;
  int a = 45;
  Serial.println((unsigned long)&a);
}

void loop()
{
}
whether you take int b out or leave it in, the Serial monitor will say 2296. Because b complies before a taking b out all together should change the address of a. but it doesn't.

sterretje

The c language specification does NOT define the size of any data types
It's not what I said (or tried to say ;))
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

nickgammon

At the top correct?
No, at the bottom, depending on what you mean by "top". The stack starts at the top (the highest possible address) and the heap starts just after all the global variables allocated by the compiler.

The stack grows downwards (the hardware manages that) and the heap jumps around in a heap-like manner from its starting point, hopefully not clashing with the stack. Of course, the heap starting point doesn't change, but there may be gaps in the allocated memory, which is what this thread is about.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

PaulS

Quote
whether you take int b out or leave it in, the Serial monitor will say 2296. Because b complies before a taking b out all together should change the address of a. but it doesn't.
You don't actually use b, so the compiler pitches it into the bit bucket.

If you actually used b, for any reason, such as printing it, the compiler would be forced to keep b around.
The art of getting good answers lies in asking good questions.

Thomas499

So i'm going over the .h file from the AvrHeap library. I'm trying to understand how the library works, but it does a few things I've never seen before. Below is the code, and beside that is coments that I put in that explains what I understand, and what I don't.
Code: [Select]
#ifndef AVRHEAP_H
#define AVRHEAP_H
//
//    FILE: Avrheap.h
//  AUTHOR: Rob dot Tillaart at gmail dot com
// VERSION: 0.1.04
// PURPOSE: heap library for Arduino (AVR)
// HISTORY: See avrheap.cpp
//
// Released to the public domain
//

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h" // if modern arduino use this file
#else
#include "WProgram.h" // if old school crap, use this
#endif

#include "Printable.h" // is this why instead of using Serial.print this library uses p.print? Is there any advantage to doing it this way?

#define AVRHEAP_LIB_VERSION "0.1.04"

class Avrheap : public Printable // tap into Printable library and gain access to all their publics, and protecteds... cant touch privates dirrectly
{
public:
    Avrheap(); // initialize

    bool     isFragmented();
    uint16_t freeListCount();
    uint16_t freeListSize();
    void     freeListWalk(bool withDump = true); // I thouht you couldn't assign values in .h file? I thought you could only declare them here, then assign them in the initializer?
    uint16_t freeListLargest();

    uint16_t startAddress();

    void     dumpHeap(uint16_t count); // unit16_t is the same thing as int. only difference is it assigns the same value for any operating system. On uno int by default is 16, on due int by default is 32.
    size_t   heapWalk(Print& p, bool withDump = true) const; // what part of this is it assigning const? withDump=true, or the address of p?
    size_t   heapWalk(bool withDump = true);                 // never seen size_t before... what does this mean?
    virtual size_t printTo(Print& p) const;                       // kind of explains virtual https://www.youtube.com/watch?v=DudHooleNVg&list=PLDED25B8DC0FEF9A1&index=56

private:
    bool     inFreeList(uint16_t addr);
};
                                                            // ================== This is in header file... but it isn't in public, private, or protected.. What is this?
size_t hNibble(Print& p, byte val);
size_t hByte(Print& p, byte val);
size_t hWord(Print& p, uint16_t val);
size_t dumpR(Print& p, byte* adr, int len);
size_t dumpAlloced(Print& p, byte *ptr, bool withDump = true);
size_t dumpAlloced(byte *ptr, bool withDump = true);

#endif

BulldogLowell

Quote
void     freeListWalk(bool withDump = true); // I thouht you couldn't assign values in .h file? I thought you could only declare them here, then assign them in the initializer?
You can certainly assign a default value to a member object of a class.  The fact that the class definition is is in a header wouldn't affect that.

Quote
// ================== This is in header file... but it isn't in public, private, or protected.. What is this?
are the function prototypes which aren't class member defined in the implementation file?

Thomas499

Quote
are the function prototypes which aren't class member defined in the implementation file?
I remember going over prototypes in bucks youtube c++ tutorials. I watched 60 of them yesterday but it hasn't all sunk in yet. In every youtube tutorial i've seen though, they say you have to intilize the class... in the cpp file below, they don't though, or at least not in any way similar to the way they do in the tuturials. Also, is there a benifit to using p.print() method instead of the normal Serial.print() method? Is it easier to transfer code to non arduino devices by using p.print()? Im trying to figure out why this library does so many things differently than the tutorials do.
Code: [Select]
//#include "Avrheap.h"

struct __freelist  // WTF where is the initializer? Can you skip the initializer by using size_t in place of where (nameofclass):: should be?
{
    size_t size;
    struct __freelist *next; // neither next nor freelist was mentioned or declared in .h file
};

extern struct   __freelist *__flp;
extern uint16_t __heap_start;
extern uint16_t *__brkval;
extern char     *__malloc_heap_start;
extern char     *__malloc_heap_end;
extern size_t   __malloc_margin;
extern uint16_t __data_start;
extern uint16_t __data_end;
extern uint16_t __bss_start;
extern uint16_t __bss_end;


size_t hNibble(Print& p, byte val)
{
    return p.write(val + (val<10 ? '0' : 'A'-10));
}

size_t hByte(Print& p, byte val)
{
    size_t len = hNibble(p, val >> 4);
    return len + hNibble(p, val & 0x0F);
}

size_t hWord(Print& p, uint16_t val)
{
    size_t len = hByte(p, (byte)(val >> 8));
    return len + hByte(p, (byte)(val & 0xFF) );
}

size_t dumpR(Print& p, byte* adr, int len)
{
    size_t glen = 0;
    byte idx;
    if (!len)
    {
        len = 16;
    }
    for (; len > 0; len -= 16, adr += 16)
    {
        glen += hWord(p, (uint16_t)adr);
        glen += p.print(F(": "));
        for (idx = 0; idx < 16; idx++)
        {
            if (idx < len )
            {
                glen += hByte(p, adr[idx]);
                glen += p.write(' ');
            } else {
                glen += p.print(F("   "));
            }
        }
        glen += p.write('\'');
        for (idx = 0; (idx < 16) && (idx < len); idx++)
        {
            glen += p.write(adr[idx] < 0x20 ? '.' : adr[idx]);
        }
        glen += p.write('\'');
        glen += p.println();
    }
    return glen;
}

size_t dumpAlloced(byte *ptr, bool withDump)
{
    return dumpAlloced(Serial, ptr, withDump);
}

size_t dumpAlloced(Print& p, byte *ptr, bool withDump)
{
    size_t len = hWord(p, (uint16_t)ptr);
    if (!ptr)
    {
        len += p.println(F(": NULL"));
    } else {
        size_t size = *(size_t*)(ptr-sizeof(size_t));
        if (size < __malloc_margin)
        {
            len += p.print(F(": size "));
            len += p.println(size);
        } else {
            len += p.print(F(": invalid size "));
            len += p.println(size);
            size = 16;
        }
        if (withDump)
        {
            len += dumpR(p, ptr, size);
            len += p.println();
        }
    }
    return len;
}


Avrheap::Avrheap()
{
};

bool Avrheap::isFragmented()
{
    return freeListCount() > 0;
};

uint16_t Avrheap::freeListCount()
{
    uint16_t count = 0;
    for (struct __freelist*  p = __flp; p; p = p->next) count++;
    return count;
}

uint16_t Avrheap::freeListSize()
{
    uint16_t total = 0;
    for (struct __freelist*  p = __flp; p; p = p->next)
    {
        total += 2;     // malloc size
        total += (uint16_t) p->size;
    }
    return total;
}

void Avrheap::freeListWalk(bool withDump)
{
    int elements = freeListCount();
    Serial.print(F("\nFreeList: "));
    Serial.print(isFragmented() ? F("fragmented") : F("clean"));
    Serial.print(F(", count "));
    Serial.print(elements);
    Serial.print(F(", largest "));
    Serial.print(freeListLargest());
    Serial.print(F(", total size "));
    Serial.println(freeListSize());
    Serial.println();
    if (elements)
    {
        for (struct __freelist* p = __flp; p; p = p->next)
        {
            hWord(Serial, (uint16_t)p);
            Serial.print(F(": size "));
            Serial.print((uint16_t)p->size);
            Serial.print(F(" next "));
            hWord(Serial, (uint16_t)p->next);
            Serial.println();
            if (withDump)
            {
                dumpR(Serial, ((byte*)p)+2, p->size);
                Serial.println();
            }
        }
    }
}

uint16_t Avrheap::startAddress()
{
    return (uint16_t) &__heap_start;
}

void Avrheap::dumpHeap(uint16_t count)
{
    hWord(Serial, (uint16_t)RAMEND);
    Serial.println(F(" RAMEND"));
    hWord(Serial, (uint16_t)SP);
    Serial.println(F(" SP"));
    hWord(Serial, (uint16_t)__brkval);
    Serial.println(F(" __brkval"));
    hWord(Serial, (uint16_t)__malloc_heap_end);
    Serial.println(F(" __malloc_heap_end"));
    hWord(Serial, (uint16_t)__malloc_heap_start);
    Serial.println(F(" __malloc_heap_start"));
    hWord(Serial, (uint16_t)&__heap_start);
    Serial.println(F(" __heap_start"));
    hWord(Serial, (uint16_t)&__bss_end);
    Serial.println(F(" __bss_end"));
    hWord(Serial, (uint16_t)&__bss_start);
    Serial.println(F(" __bss_start"));
    hWord(Serial, (uint16_t)&__data_end);
    Serial.println(F(" __data_end"));
    hWord(Serial, (uint16_t)&__data_start);
    Serial.println(F(" __data_start"));
    hWord(Serial, (uint16_t)__malloc_margin);
    Serial.println(F(" __malloc_margin"));
    Serial.println();
    Serial.println(F("start of heap"));
    Serial.println();
    dumpR(Serial, (byte*)startAddress(), count);
}

size_t Avrheap::heapWalk(bool withDump) {
    return heapWalk(Serial, withDump);
}

// EXPERIMENTAL
size_t Avrheap::heapWalk(Print& pr, bool withDump) const
{
    byte* p = (byte*) &__heap_start;
    struct __freelist* fp = __flp;

    size_t len = pr.println(F("Heap\n"));
    while ((int)p < (int)__brkval)
    {
        len += hWord(pr, (uint16_t)p); // p+2 ?
        len += pr.write(' ');
        len += pr.print(*p, DEC);
        if ( (fp != NULL) && ((uint16_t)p == (uint16_t)fp))
        {
            len += pr.print(F(" (free)"));
            fp = fp->next;
        }
        len += pr.println();
        if (withDump) {
            len += dumpR(pr, p, *p+2);
            len += pr.println();
        }
        p += (byte) *p + 2;
    }
    return len;
}


bool Avrheap::inFreeList(uint16_t addr)
{
    for (struct __freelist* p = __flp; p; p = p->next)
    {
        if (addr == (uint16_t)p) return true;
    }
    return false;
}

uint16_t Avrheap::freeListLargest()
{
    uint16_t largest = 0;
    for (struct __freelist*  p = __flp; p; p = p->next)
    {
        largest = max(largest, (uint16_t) p->size);
    }
    return largest;
}

size_t Avrheap::printTo(Print& p) const
{
    size_t len = heapWalk(p, true);
    return len;
}




// --- END OF FILE ---

BulldogLowell

#26
Dec 17, 2015, 06:44 pm Last Edit: Dec 17, 2015, 06:48 pm by BulldogLowell
so the typical C++ program looks something like this:

Code: [Select]
#myIncludes            // include library and other files

void myFunction(myArgs);  // declare your functions in this prototype format

int myVariable;                  // declare /initialize global objects

int main()                         // main

{
  //do something               // arduino calls setup() and loop() here
  //
  return 0;
}

void myFunction(myArgs)  // function definition
{

}


So think of the header file (myLibrary.h) as sort of inserting itself at the top of the C++ file (the 'header') and the implementation file (myLibrary.cpp) tucking in after the main() function... the implementation of the objects defined in the header.

Sort of thing...

EDIT: so, therefore you can declare objects (like classes or functions) in the header, like Rob did.

-dev

Quote from: BulldogLowell
Quote from Thomas499: "I thought you couldn't assign values in .h file? I thought you could only declare them here, then assign them in the initializer?

You can certainly assign a default value to a member object of a class.  The fact that the class definition is is in a header wouldn't affect that.
Not exactly.  The code Thomas499 referenced is a default argument value, not an initial value for member data.  If omitted by the caller, the compiler "secretly" passes that value into the method.  And like he said, you can only declare member data in the class definition.  Its initial value must be set in the implementation:

   Avrheap::Avrheap() : val( MAX_VALUE ) {}

or

   Avrheap::Avrheap()
   {
     val = MAX_VALUE;
   }

or (for class data) the storage definition:

   int Avrheap::classDataName = MAX_VALUE;

There is one exception for const class data:

   static const int MAX_VALUE = 100;

(The static keyword indicates class data instead of instance data.)  This is very close to

   #define MAX_VALUE (100)

...except it doesn't get replaced by the preprocessor.

Cheers,
/dev
Really, I used to be /dev.  :(

Thomas499

#28
Dec 17, 2015, 08:17 pm Last Edit: Dec 17, 2015, 08:27 pm by Thomas499
I'm kind of understanding this, but I still have a lot to learn. I'm going to keep trying, but if someone could help me with this, it would mean a lot because I don't even understand half the stuff in the library.

I want to do this
Code: [Select]
void Avrheap::freeListWalk(bool withDump)
{   unsigned int largestFrag=0;
    unsigned int currentUfRam=0;
    int elements = freeListCount();
    Serial.print(F("\nFreeList: "));
    Serial.print(isFragmented() ? F("fragmented") : F("clean"));
    Serial.print(F(", count "));
    Serial.print(elements);
    Serial.print(F(", largest "));
    largestFrag=freeListLargest();
    Serial.print(largestFrag);
    Serial.print(F(", total size "));
    currentUfRam=freeListSize(); // total size, will find out unfragmented Ram in next step
    Serial.print(currentUfRam);
    Serial.print(F(", Largest RamSpace is "));
     __heap_start,*__brkval;
    int v;
    currentUfRam=(((int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval))-currentUfRam);
    if (currentUfRam>largestFrag)
    Serial.println(currentUfRam);
    else
    Serial.println(largestFrag);
without printing off all the stuff the library prints. For example, when I use the library I don't want to see this stuff
Quote
02AE: size 58 next 030E
02B0: 0E 03 77 6E 77 7D FE 33 5F D6 74 7B FE F6 BF 1C '..wnw}þ3_Öt{þö¿.'
02C0: 74 DA 32 00 0E 00 FA 02 69 FF 39 57 BD AF E7 DC 'tÚ2...ú.iÿ9W½¯çÜ'
02D0: 2D 77 37 86 0A 00 94 B7 FD 40 AD F2 DF BB 0A 00 '-w7†.."·ý@­òß»..'
02E0: 18 00 00 00 7E 6F AC D8 9E 6F   
I just want to see
Quote
FreeList: fragmented, count 2, largest 68, total size 90, Largest RamSpace is 7402
which tells me really quickly and is easy to look at how many chuncks of fragmented ram are scattered around so I can judge if they are continuously increasing. The total fragmented space, and then the last part tells me the largest whole chunk of freeRam that I can actually use.

-----------Edit-----------
I changed my mind. This would be more ideal
Quote
FreeList: Largest RamSpace is 7402, fragmented 30%, total size 90, count 2, largest 68
That way the first thing I see is the most important thing which is the largest whole block of freeRam you can put something in. Then if that number concerns me i can just glance to the right and see what the heck is actually going on. What percent of memory is fragmented (if low then I know my program is using most of the Ram, and it isn't fragmented... if high I Know I need to see how many separate blocks of fragmented ram I'm dealing with.




BulldogLowell

not to hang crepe, but wouldn't the Observer Effect apply here?

The mere presence of the overhead of the library you are working would affect the results, no?

Go Up