diff --git a/examples/FreeRam/FreeRam.ino b/examples/FreeRam/FreeRam.ino index 9af5fe5..2a47a81 100644 --- a/examples/FreeRam/FreeRam.ino +++ b/examples/FreeRam/FreeRam.ino @@ -1,8 +1,21 @@ #include +byte * p = 0; + + +int updateStack(void) +{ + char volatile stuff[] = "updating stack!"; + stuff[0] = 'U'; + Serial.println((char *) stuff); + FREERAM_PRINT + return MU::getFreeRam(); +} + void setup() { Serial.begin(115200); + Serial.println(F( "Running " __FILE__ ", Built " __DATE__)); Serial.println(F("Starting state of the memory:")); Serial.println(); @@ -13,12 +26,19 @@ void setup() MEMORY_PRINT_END MEMORY_PRINT_HEAPSIZE + updateStack(); + Serial.println(); Serial.println(); FREERAM_PRINT; + + //byte *p = new byte[3000]; + byte *p = new byte[300]; // Uno (ATmega328) only has 2k RAM - byte *p = new byte[3000]; + if(!p) { + Serial.println(F("could not allocate bytes for p[] array!")); + } Serial.println(); Serial.println(); @@ -37,9 +57,15 @@ void setup() Serial.println(); FREERAM_PRINT; + + Serial.print(F("num STACK_COMPUTE calls: ")); + Serial.println(numStackComputeCalls); + + delete p; + p = 0; } void loop() { -} \ No newline at end of file +} diff --git a/examples/GetMemorySize/GetMemorySize.ino b/examples/GetMemorySize/GetMemorySize.ino new file mode 100644 index 0000000..c56ab0f --- /dev/null +++ b/examples/GetMemorySize/GetMemorySize.ino @@ -0,0 +1,53 @@ +#include + +// Simple example to report memory sizes + +void reportAllocation(int numBytes) { + + Serial.print(F("Allocating for ")); + Serial.print( numBytes ); + Serial.print(F(" bytes; ")); + + byte *p = new byte[numBytes]; + + if (p) { + Serial.println(F("...success.")); + } else { + Serial.println(F("...allocation FAILED")); + } + + MEMORY_PRINT_HEAPSIZE + FREERAM_PRINT + + Serial.println(F("\ndeleting byte array with delete")); + delete p; // don't want a memory leak! + p = 0; // don't want a dangling/obsolete pointer + + MEMORY_PRINT_HEAPSIZE + FREERAM_PRINT +} + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println(F( "Running " __FILE__ ", Built " __DATE__)); + + Serial.println(F("\nStarting conditions")); + MEMORY_PRINT_TOTALSIZE + MEMORY_PRINT_HEAPSIZE + FREERAM_PRINT + + Serial.println(F("\nallocate a byte array with new; too big to fit in RAM?")); + reportAllocation(3000); + + Serial.println(F("\nallocate smaller byte array with new (it should fit)")); + reportAllocation(300); + + Serial.println(F("\nFinal conditions")); + MEMORY_PRINT_HEAPSIZE + FREERAM_PRINT +} + +void loop() { + // User reads output from setup(). +} diff --git a/examples/Stack/Stack.ino b/examples/Stack/Stack.ino index 084c8d0..d2f8ce5 100644 --- a/examples/Stack/Stack.ino +++ b/examples/Stack/Stack.ino @@ -15,9 +15,30 @@ void subPointer(rhaaa *apSample); void subSmartPointer(rhaaa &aSample); void subConstSmartPointer(const rhaaa &aSample); +inline void reportMemoryInfo(void) +{ + STACK_COMPUTE + + MEMORY_PRINT_START + MEMORY_PRINT_HEAPSTART + MEMORY_PRINT_HEAPEND + MEMORY_PRINT_STACKSTART + MEMORY_PRINT_END + MEMORY_PRINT_STACKSIZE + + STACK_PRINT + + STACKPAINT_PRINT + Serial.println(); +} + void setup() { Serial.begin(115200); + Serial.println(F( "Running " __FILE__ ", Built " __DATE__)); + + reportMemoryInfo(); + //STACK_COMPUTE // An instance of the sample is declared, and the string is filled with // some string to see how to access to it inside functions ! @@ -31,14 +52,7 @@ void setup() Serial.println(F("Starting state of the memory:")); Serial.println(); - MEMORY_PRINT_START - MEMORY_PRINT_HEAPSTART - MEMORY_PRINT_HEAPEND - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - - STACKPAINT_PRINT + reportMemoryInfo(); Serial.println(); Serial.println(); @@ -68,7 +82,7 @@ void setup() // No data as argument, nut a nig array of doubles inside the function... subLocalData(); - STACKPAINT_PRINT + //STACKPAINT_PRINT Serial.println(); Serial.println(); @@ -76,12 +90,10 @@ void setup() Serial.println(F("Ending state of the memory:")); Serial.println(); - MEMORY_PRINT_START - MEMORY_PRINT_HEAPSTART - MEMORY_PRINT_HEAPEND - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE + reportMemoryInfo(); + + Serial.print(F("num STACK_COMPUTE calls: ")); + Serial.println(numStackComputeCalls); Serial.println(); Serial.println(); @@ -91,10 +103,7 @@ void subFull(rhaaa aSample) { Serial.println("subFull"); Serial.println(aSample.text); - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - STACK_PRINT + reportMemoryInfo(); Serial.println(); } @@ -102,10 +111,7 @@ void subPointer(rhaaa *apSample) { Serial.println("subPointer"); Serial.println(apSample->text); - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - STACK_PRINT + reportMemoryInfo(); Serial.println(); } @@ -113,10 +119,7 @@ void subSmartPointer(rhaaa &aSample) { Serial.println("subSmartPointer"); Serial.println(aSample.text); - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - STACK_PRINT + reportMemoryInfo(); Serial.println(); } @@ -124,10 +127,7 @@ void subConstSmartPointer(const rhaaa &aSample) { Serial.println("subConstSmartPointer"); Serial.println(aSample.text); - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - STACK_PRINT + reportMemoryInfo(); Serial.println(); } @@ -141,14 +141,12 @@ void subLocalData() v[i] = (double)i; Serial.println(v[10]); - MEMORY_PRINT_STACKSTART - MEMORY_PRINT_END - MEMORY_PRINT_STACKSIZE - STACK_PRINT + reportMemoryInfo(); Serial.println(); } void loop() { -} \ No newline at end of file +} + diff --git a/extras/tests/MemoryUsageTest/MemoryUsageTest.ino b/extras/tests/MemoryUsageTest/MemoryUsageTest.ino new file mode 100644 index 0000000..ea412e2 --- /dev/null +++ b/extras/tests/MemoryUsageTest/MemoryUsageTest.ino @@ -0,0 +1,246 @@ +// MemoryUsageTest.ino +// +// Memory Usage unit tests. Expected to run on Arduino UNO (ATMega328) +// (tests may fail on other boards) +// +// REQUIRES: MemoryUsage, AUnit libraries. +// +#include + +#include +using namespace MU; + +STACK_DECLARE + +/////////////////////////////////////////////////////////////// +test(heapStart) { + int heapStart = getHeapStart(); + int dataStart = getDataStart(); + int memoryEnd = getMemoryEnd(); + int approxGlobalSize = 350; + assertMore(heapStart, dataStart + approxGlobalSize); // heap after globals + assertLess(heapStart, memoryEnd); + //assertEqual(908,heapStart); // too specific! why this value? +} + +/////////////////////////////////////////////////////////////// +test(heapEnd) { + int heapEnd = getHeapEnd(); + int heapStart = getHeapStart(); + int memoryEnd = getMemoryEnd(); + + assertMoreOrEqual(heapEnd, heapStart); + assertLess(heapEnd, memoryEnd); + + int reasonableStack = 100; + assertLess(heapEnd, (memoryEnd - reasonableStack)); + //assertEqual(912,he); // too specific! why this? + + { + String foo(F("this is something to put on the heap")); + int heapWithString = getHeapEnd(); + assertMore(heapWithString, heapEnd); + assertEqual(39, heapWithString - heapEnd); // too specific? + // foo goes out of scope (and cleaned up) + } + int heapAfterStringGone = getHeapEnd(); + assertEqual(heapEnd, heapAfterStringGone); +} + +/////////////////////////////////////////////////////////////// +test(heapSize) { + int heapSize = getHeapSize(); + assertEqual(0, heapSize); + + { + String foo(F("this is something to put on the heap")); + int heapWithString = getHeapSize(); + assertMore(heapWithString, heapSize); + assertEqual(39, heapWithString - heapSize); // too specific? + // foo goes out of scope (and cleaned up) + } + int finalHeapSize = getHeapSize(); + assertEqual(heapSize, finalHeapSize); +} + +/////////////////////////////////////////////////////////////// +test(stackSize) { + int ss = getStackSize(); + assertEqual(12, ss); // too specific? +} + +/////////////////////////////////////////////////////////////// +void __attribute__ ((noinline)) STACK_COMPUTE_function(void) +{ + // treat this as a real function to ensure stack is up-to-date + STACK_COMPUTE; +} + +/////////////////////////////////////////////////////////////// +void __attribute__ ((noinline)) BIG_STACK_COMPUTE_function(void) +{ + // noinline: + // treat this is a real function to exersise return stack + // do not optimize the function call/return away! + + // volatile: + // make this variable ready to change from outside this call chain + // do not optimize the data away because it doesn't seem to be used! + // + char volatile foo[] = "something on the stack"; + String moreStuff = (char *) foo; // on the heap too + + STACK_COMPUTE_function(); +} + +/////////////////////////////////////////////////////////////// +test(stackMaxSize) { + int stackSize1 = getMaxStackSize(); + assertEqual(2, stackSize1); + + STACK_COMPUTE_function(); // first call to STACK_COMPUTE in this sketch + int stackSize2 = getMaxStackSize(); + assertMore(stackSize2, stackSize1); + + int stackSize3 = 0; + + BIG_STACK_COMPUTE_function(); + stackSize3 = getMaxStackSize(); + assertMore(stackSize3, stackSize2); + assertEqual(stackSize2 + 37, stackSize3); // too specific? + + STACK_COMPUTE_function(); + int stackSize4 = getMaxStackSize(); + assertEqual(stackSize3, stackSize4); // no shrinking +} + +/////////////////////////////////////////////////////////////// +int __attribute__ ((noinline)) exerciseStack(void) { + return getStackStart(); +} + +/////////////////////////////////////////////////////////////// +int __attribute__ ((noinline)) useMoreFreeRAM(void) { + const int numFoos = 100; + int volatile foo[numFoos] = {0}; // use stack -- don't optimize away + for (int i = 0; i < numFoos; i++) { + foo[i] = getFreeRam(); + } + foo[numFoos - 1] = getFreeRam(); + return foo[numFoos - 1]; +} + +/////////////////////////////////////////////////////////////// +test(freeRam) { + int frBefore = getFreeRam(); + assertMore(frBefore, 100); + assertLess(frBefore, (int) getRamSize()); + + //int currentStack = exerciseStack(); + exerciseStack(); + frBefore = getFreeRam(); + assertMore(frBefore, 100); + assertLess(frBefore, (int) getRamSize()); + + int frAfter = useMoreFreeRAM(); + + assertLess(frAfter, frBefore); + int deltaBytes = frAfter - frBefore; + assertEqual(deltaBytes, -208); // too specific? +} + +/////////////////////////////////////////////////////////////// +test(ramSize) { + int ramSize = getRamSize(); + assertEqual(2048, ramSize); // for ATmega328 +} + +/////////////////////////////////////////////////////////////// +test(dataStart) { + int ds = getDataStart(); + assertEqual(0x100, ds); // for ATmega328 +} + +/////////////////////////////////////////////////////////////// +test(memoryEnd) { + int me = getMemoryEnd(); + + // NOTE memory end is the last byte of RAM + // -- not the byte after + int expectedLength = 2048-1; // 2047 + + assertEqual(0x100 + expectedLength, me); // for ATmega328 +} + +/////////////////////////////////////////////////////////////// +int __attribute__ ((noinline)) exerciseStackBigger(void) { + const int numFoos = 100; + int volatile foo[numFoos] = {0}; + for (int i = 0; i < numFoos; i++) { + foo[i] = exerciseStack(); + } + foo[0] = exerciseStack(); + return foo[0]; +} + +/////////////////////////////////////////////////////////////// +test(stackStart) { + int stackStart = getStackStart(); + int ds = getDataStart(); + int me = getMemoryEnd(); + assertMore(stackStart, ds + 350); // should be after globals + assertLess(stackStart, me); + assertMore(stackStart, me - 50); // stack should not be very big here + + int moreStack = exerciseStackBigger(); // stack grows DOWN + + assertLess(moreStack, stackStart); + assertMore(moreStack, stackStart - 230); + + assertEqual(moreStack, stackStart - 210); // too specific? +} + +/////////////////////////////////////////////////////////////// +test(stackWithTop) { + char top = '!'; // top of stack + int stackStart = (int) ⊤ + int moreStack; // why aren't these ints taking up room? register vars? + { + char top2 = '!'; + moreStack = (int) &top2; + } + int stackChange = stackStart - moreStack; // stack grows DOWN + assertEqual((int) sizeof(char), stackChange); +} + +/////////////////////////////////////////////////////////////// +test(stack_SP_vsTop) { + int stackStart; + int stackStartSP; + char top = '!'; // top of stack (used) + stackStart = (int) (&top); + stackStartSP = getStackStart(); // next (unused) byte + + assertEqual(stackStartSP + 1, stackStart); +} + +//---------------------------------------------------------------------------- +// setup() and loop() +//---------------------------------------------------------------------------- + +void setup() { + delay(1000); // wait for stability on some boards to prevent garbage Serial + Serial.begin(115200); + while (!Serial); // for the Arduino Leonardo/Micro only + + Serial.println(F( "Running " __FILE__ ", Built " __DATE__)); + Serial.println(F("This test should produce the following:")); + Serial.println( + F("12 passed, 0 failed, 0 skipped, 0 timed out, out of 12 test(s).") + ); + Serial.println(F("----")); +} + +void loop() { + aunit::TestRunner::run(); +} diff --git a/src/MemoryUsage.cpp b/src/MemoryUsage.cpp index 74fa142..2967961 100644 --- a/src/MemoryUsage.cpp +++ b/src/MemoryUsage.cpp @@ -68,12 +68,14 @@ void mu_StackPaint(void) #endif } +int numStackComputeCalls = 0; + /// Checks the first undecorated byte. uint16_t mu_StackCount(void) { uint8_t *p = (__brkval == 0 ? (uint8_t *) &__heap_start : __brkval); - while (*p == STACK_CANARY && (int) p <= SP) + while (*p == STACK_CANARY && p <= (uint8_t *) SP) p++; return (uint16_t)RAMEND - (uint16_t)p; @@ -92,24 +94,24 @@ void SRamDisplay(void) available -= data_size + bss_size + heap_size + stack_size; - Serial.print(F("+----------------+ ")); Serial.print((int)&__data_start); Serial.println(" (__data_start)"); + Serial.print(F("+----------------+ ")); Serial.print((int)&__data_start); Serial.println(F(" (__data_start)")); Serial.print(F("+ data +")); Serial.println(); Serial.print(F("+ variables + size = ")); Serial.println(data_size); - Serial.print(F("+----------------+ ")); Serial.print((int)&__data_end); Serial.println(" (__data_end / __bss_start)"); + Serial.print(F("+----------------+ ")); Serial.print((int)&__data_end); Serial.println(F(" (__data_end / __bss_start)")); Serial.print(F("+ bss +")); Serial.println(); Serial.print(F("+ variables + size = ")); Serial.println(bss_size); - Serial.print(F("+----------------+ ")); Serial.print((int)&__bss_end); Serial.println(" (__bss_end / __heap_start)"); + Serial.print(F("+----------------+ ")); Serial.print((int)&__bss_end); Serial.println(F(" (__bss_end / __heap_start)")); Serial.print(F("+ heap + size = ")); Serial.println(heap_size); - Serial.print(F("+----------------+ ")); Serial.print((int)heap_end); Serial.println(" (__brkval if not 0, or __heap_start)"); + Serial.print(F("+----------------+ ")); Serial.print((int)heap_end); Serial.println(F(" (__brkval if not 0, or __heap_start)")); Serial.print(F("+ +")); Serial.println(); Serial.print(F("+ +")); Serial.println(); Serial.print(F("+ FREE RAM + size = ")); Serial.println(available); Serial.print(F("+ +")); Serial.println(); Serial.print(F("+ +")); Serial.println(); - Serial.print(F("+----------------+ ")); Serial.print((int)SP); Serial.println(" (SP)"); + Serial.print(F("+----------------+ ")); Serial.print((int)SP); Serial.println(F(" (SP)")); Serial.print(F("+ stack + size = ")); Serial.println(stack_size); - Serial.print(F("+----------------+ ")); Serial.print((int)RAMEND); Serial.println(" (RAMEND / __stack)"); + Serial.print(F("+----------------+ ")); Serial.print((int)RAMEND); Serial.println(F(" (RAMEND / __stack)")); Serial.println(); Serial.println(); -} \ No newline at end of file +} diff --git a/src/MemoryUsage.h b/src/MemoryUsage.h index 0cc1f2b..23516e7 100644 --- a/src/MemoryUsage.h +++ b/src/MemoryUsage.h @@ -114,29 +114,99 @@ extern uint8_t *__heap_end; extern uint8_t *__bss_start; extern uint8_t *__bss_end; +extern int numStackComputeCalls; +extern int mu_stack_size; + // // Memory addresses // +namespace MU { + // start of RAM: + // ... initialized data + // ... then bss (uninitialized data) + // ... then heap + // ... then FREE RAM + // ... then stack + // ... then end of RAM + + inline unsigned int getDataStart(void) + { + return (unsigned int) &__data_start; // start RAM + } + + inline unsigned int getHeapStart(void) + { + return (unsigned int) &__heap_start; + } + + inline unsigned int getHeapEnd(void) + { + unsigned int heapEnd = getHeapStart(); + if (__brkval) + heapEnd = (unsigned int)__brkval; + return heapEnd; + } + + inline unsigned int getHeapSize(void) + { + return getHeapEnd() - getHeapStart(); + } + + inline unsigned int getStackStart(void) + { + // next UNUSED entry + // active stack above here. Stack grows DOWN from RAMEND + return (unsigned int) SP; + } + + inline unsigned int getFreeRam(void) + { + return getStackStart() - getHeapEnd(); + } + + inline unsigned int getMemoryEnd(void) + { + return (unsigned int) RAMEND; // last byte! (not the byte after) + } + + inline unsigned int getStackSize(void) + { + return getMemoryEnd() - getStackStart(); + } + + inline unsigned int getMaxStackSize(void) + { + return mu_stack_size; + } + + inline unsigned int getRamSize(void) + { + return (getMemoryEnd() + 1) - getDataStart(); + } +}; // end namespace MU + +#define MU_PRINT(txt, value) {Serial.print((txt)); Serial.println((value));} /// Print data start on serial console. -#define MEMORY_PRINT_START { Serial.print(F("Data start:")); Serial.println((int) &__data_start); } +#define MEMORY_PRINT_START MU_PRINT(F("Data start:"), MU::getDataStart()) /// Print data end / heap start on serial console. -#define MEMORY_PRINT_HEAPSTART { Serial.print(F("Heap start:")); Serial.println((int)&__heap_start); } +#define MEMORY_PRINT_HEAPSTART MU_PRINT(F("Heap start:"), MU::getHeapStart()) /// Print heap end / free ram area on serial console. -#define MEMORY_PRINT_HEAPEND { Serial.print(F("Heap end:")); Serial.println(__brkval == 0 ? (int)&__heap_start : (int)__brkval); } +#define MEMORY_PRINT_HEAPEND MU_PRINT(F("Heap end:"), MU::getHeapEnd()) /// Print free ram end / stack start on serial console. -#define MEMORY_PRINT_STACKSTART { Serial.print(F("Stack start:")); Serial.println((int) SP); } +#define MEMORY_PRINT_STACKSTART MU_PRINT(F("Stack start:"), MU::getStackStart()) /// Print end of memory on serial console. -#define MEMORY_PRINT_END { Serial.print(F("Stack end:")); Serial.println((int) RAMEND); } +#define MEMORY_PRINT_END MU_PRINT(F("Stack end:"), MU::getMemoryEnd()) /// Print heap size on serial console. -#define MEMORY_PRINT_HEAPSIZE { Serial.print(F("Heap size:")); Serial.println((int) (__brkval == 0 ? (int)&__heap_start : (int)__brkval) - (int)&__heap_start); } +#define MEMORY_PRINT_HEAPSIZE MU_PRINT(F("Heap size:"),MU::getHeapSize()) /// Print stack size on serial console. -#define MEMORY_PRINT_STACKSIZE { Serial.print(F("Stack size:")); Serial.println((int) RAMEND - (int)SP); } +#define MEMORY_PRINT_STACKSIZE MU_PRINT(F("Stack size:"),MU::getStackSize()) /// Print free ram size on serial console. -#define MEMORY_PRINT_FREERAM { Serial.print(F("Free ram:")); Serial.println((int) SP - (int) (__brkval == 0 ? (int)&__heap_start : (int)__brkval)); } +#define MEMORY_PRINT_FREERAM MU_PRINT(F("Free ram:"),; MU::getFreeRam()) /// Print total SRAM size on serial console. -#define MEMORY_PRINT_TOTALSIZE { Serial.print(F("SRAM size:")); Serial.println((int) RAMEND - (int) &__data_start); } +#define MEMORY_PRINT_TOTALSIZE MU_PRINT(F("SRAM size:"),MU::getRamSize()) + /// Displays the 'map' of the current state of the Arduino's SRAM memory on the Serial console. void SRamDisplay(void); @@ -146,10 +216,18 @@ void SRamDisplay(void); // /// Must be used only one time, outside any function. -#define STACK_DECLARE unsigned int mu_stack_size = (RAMEND - SP); +#define STACK_DECLARE int mu_stack_size = ((int) RAMEND - (int) SP); /// Must be called to update the current maximum size of the stack, at each function beginning. -#define STACK_COMPUTE { mu_stack_size = (RAMEND - SP) > mu_stack_size ? (RAMEND - SP) : mu_stack_size;} +//#define STACK_COMPUTE { mu_stack_size = (RAMEND - SP) > mu_stack_size ? (RAMEND - SP) : mu_stack_size;} +inline void stackCompute(void) { + int currentStack = (int) SP; + int currentSize = (int) RAMEND - currentStack; + mu_stack_size = max(currentSize, mu_stack_size); + numStackComputeCalls++; + //Serial.println(F("COMPUTING STACK...")); +} +#define STACK_COMPUTE stackCompute(); /// Compute the current maximum and show it now with customized text. #define STACK_PRINT_TEXT(text) { STACK_COMPUTE; Serial.print(text); Serial.println(mu_stack_size); } @@ -178,10 +256,6 @@ int mu_freeRam(void); uint16_t mu_StackCount(void); /// Compute the current maximum and show it now with customized text. -#define STACKPAINT_PRINT_TEXT(text) { Serial.print(text); Serial.println(mu_StackCount()); } - -/// Compute the current maximum and show it now with default text. -#define STACKPAINT_PRINT STACKPAINT_PRINT_TEXT(F("Stack Maximum Size (Painting method): ")); - +#define STACKPAINT_PRINT MU_PRINT(F("Stack Maximum Size (Painting method):"),mu_StackCount()) #endif