diff --git a/TaiChi/MemoryUsage.cpp b/TaiChi/MemoryUsage.cpp new file mode 100644 index 0000000..107be19 --- /dev/null +++ b/TaiChi/MemoryUsage.cpp @@ -0,0 +1,115 @@ +/* +MemoryUsage.c - MemoryUsage library +Copyright (c) 2015 Thierry Paris. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/*! \file MemoryUsage.cpp + +Main library code file. +*/ + +#include "Arduino.h" +#include "MemoryUsage.h" + +/// Thanks to adafruit : https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory +int mu_freeRam() +{ + //extern int __heap_start, *__brkval; + int v; + return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); +} + +/// Copy / adaptation of the library StackPaint available here : https://github.com/WickedDevice/StackPaint + +#define STACK_CANARY 0xc5 + +void mu_StackPaint(void) __attribute__((naked)) __attribute__((section(".init1"))); + + +/// Function called before any other function. +void mu_StackPaint(void) +{ +#if 1 + uint8_t *p = &_end; + + while (p <= &__stack) + { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile ( + " ldi r30,lo8(_end)\n" + " ldi r31,hi8(_end)\n" + " ldi r24,lo8(0xc5)\n" // STACK_CANARY = 0xc5 + " ldi r25,hi8(__stack)\n" + " rjmp .cmp\n" + ".loop:\n" + " st Z+,r24\n" + ".cmp:\n" + " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" + " brlo .loop\n" + " breq .loop"::); +#endif +} + +/// 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) + p++; + + return (uint16_t)RAMEND - (uint16_t)p; +} + +/// Modified function from http://www.avr-developers.com/mm/memoryusage.html +void SRamDisplay(void) +{ + int data_size = (int)&__data_end - (int)&__data_start; + int bss_size = (int)&__bss_end - (int)&__data_end; + int heap_end = (int) (__brkval == 0 ? (uint8_t *) &__heap_start : __brkval); + //int heap_end = (int)SP - (int)&__malloc_margin; + int heap_size = heap_end - (int)&__bss_end; + int stack_size = RAMEND - (int)SP + 1; + int available = (RAMEND - (int)&__data_start + 1); + + available -= data_size + bss_size + heap_size + stack_size; + + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)&__data_start); NeoSerial.println(" (__data_start)"); + NeoSerial.print(F("+ data +")); NeoSerial.println(); + NeoSerial.print(F("+ variables + size = ")); NeoSerial.println(data_size); + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)&__data_end); NeoSerial.println(" (__data_end / __bss_start)"); + NeoSerial.print(F("+ bss +")); NeoSerial.println(); + NeoSerial.print(F("+ variables + size = ")); NeoSerial.println(bss_size); + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)&__bss_end); NeoSerial.println(" (__bss_end / __heap_start)"); + NeoSerial.print(F("+ heap + size = ")); NeoSerial.println(heap_size); + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)heap_end); NeoSerial.println(" (__brkval if not 0, or __heap_start)"); + NeoSerial.print(F("+ +")); NeoSerial.println(); + NeoSerial.print(F("+ +")); NeoSerial.println(); + NeoSerial.print(F("+ FREE RAM + size = ")); NeoSerial.println(available); + NeoSerial.print(F("+ +")); NeoSerial.println(); + NeoSerial.print(F("+ +")); NeoSerial.println(); + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)SP); NeoSerial.println(" (SP)"); + NeoSerial.print(F("+ stack + size = ")); NeoSerial.println(stack_size); + NeoSerial.print(F("+----------------+ ")); NeoSerial.print((int)RAMEND); NeoSerial.println(" (RAMEND / __stack)"); + + NeoSerial.println(); + NeoSerial.println(); +} \ No newline at end of file diff --git a/TaiChi/MemoryUsage.h b/TaiChi/MemoryUsage.h new file mode 100644 index 0000000..5342a20 --- /dev/null +++ b/TaiChi/MemoryUsage.h @@ -0,0 +1,188 @@ +/* +MemoryUsage.h - MemoryUsage library V2.10 +Copyright (c) 2015 Thierry Paris. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MemoryUsage_h__ +#define __MemoryUsage_h__ + +#include +#include + +/*! \mainpage + +A full explanation in french can be read at http://www.locoduino.org/ecrire/?exec=article&action=redirect&type=article&id=149 . + +Roughly, the SRAM memory is divided into four areas: the static data, the heap, the free ram and the stack. + +The static data size is given by the compiler itself after the building. this is filled by all variables and +arrays declared in global scope, or with 'static' keyword. + +The heap is filled with all the dynamic allocations done with 'new' keyword or 'malloc' functions. + +The stack start from the end of the SRAM area and grow and shrink downward at each function call, it stores +all the local data internal to a function, function arguments (depending of the architecture, arguments can be +stored in CPU registers to improve speed...) , and addresses for function returns to caller. + +SRAM memory +\verbatim ++---------------+------------------+---------------------------------------------+-----------------+ +| | | | | +| | | | | +| static | | | | +| data | heap | free ram | stack | +| | | | | +| | | | | +| | | | | ++---------------+------------------+---------------------------------------------+-----------------+ + _end or __heap_start __brkval SP RAMEND +\endverbatim + +Source : http://www.nongnu.org/avr-libc/user-manual/malloc.html + +MemoryUsage try to help you to find the actual memory status with differents strategies, but dont forget +that when you observe something, you change the result of the observation : execution time is consumed +by the analysis tools, and memory used will grow because of these tools ! + +1. First, there are the MACROs to show memory areas start/end addresses and actual sizes. + +2. Second, there is a display function to show a 'map' of the memory... + +3. Third, a function can give you the current size of the free ram using a stack tag, which is more accurate +than the MACRO. + +4. Fourth, an elegant way to try to understand how much size has reached the stack during execution. +It will 'decorate' the internal memory, and try to identify after a moment of execution at what place +the first byte of the memory is not anymore decorated... +The function mu_StackPaint will be called _before the setup() function of your sketch, to 'paint' or +'decorate' all the bytes of the SRAM momery with a particular code, called the CANARY... Later, a function +mu_StackCount can be called to get the actual maximum size reached by the stack by counter the byte +no more painted. +This is a copy / adaptation of the library StackPaint available here : https://github.com/WickedDevice/StackPaint + +5. And five at least, and because a stack grow and shrink continuously, the macros STACK_DECLARE / STACK_COMPUTE / STACK_PRINT +try to get the greatest size of the stack by 'sampling' the execution. +Start your code by + + #include + + STACK_DECLARE + + void setup() + ... + +then add a STACK_COMPUTE in any function that can be called : + + void subFonction() + { + double v[SIZE]; + STACK_COMPUTE; + + .... // do things + } + +and finish by printing on the console the biggest size of the stack with STACK_PRINT or STACK_PRINT_TEXT. +Be careful with this method, this introduce some code in every function of your sketch, so if the timing +is important for your applicaion, take care of it ! +*/ + +/*! \file MemoryUsage.h + +Main library header file. +*/ + +extern uint8_t _end; +extern uint8_t __stack; +extern uint8_t *__brkval; +extern uint8_t *__data_start; +extern uint8_t *__data_end; +extern uint8_t *__heap_start; +extern uint8_t *__heap_end; +extern uint8_t *__bss_start; +extern uint8_t *__bss_end; + +// +// Memory addresses +// + +/// Print data start on NeoSerial console. +#define MEMORY_PRINT_START { NeoSerial.print(F("Data start:")); NeoSerial.println((int) &__data_start); } +/// Print data end / heap start on NeoSerial console. +#define MEMORY_PRINT_HEAPSTART { NeoSerial.print(F("Heap start:")); NeoSerial.println((int)&__heap_start); } +/// Print heap end / free ram area on NeoSerial console. +#define MEMORY_PRINT_HEAPEND { NeoSerial.print(F("Heap end:")); NeoSerial.println(__brkval == 0 ? (int)&__heap_start : (int)__brkval); } +/// Print free ram end / stack start on NeoSerial console. +#define MEMORY_PRINT_STACKSTART { NeoSerial.print(F("Stack start:")); NeoSerial.println((int) SP); } +/// Print end of memory on NeoSerial console. +#define MEMORY_PRINT_END { NeoSerial.print(F("Stack end:")); NeoSerial.println((int) RAMEND); } + +/// Print heap size on NeoSerial console. +#define MEMORY_PRINT_HEAPSIZE { NeoSerial.print(F("Heap size:")); NeoSerial.println((int) (__brkval == 0 ? (int)&__heap_start : (int)__brkval) - (int)&__heap_start); } +/// Print stack size on NeoSerial console. +#define MEMORY_PRINT_STACKSIZE { NeoSerial.print(F("Stack size:")); NeoSerial.println((int) RAMEND - (int)SP); } +/// Print free ram size on NeoSerial console. +#define MEMORY_PRINT_FREERAM { NeoSerial.print(F("Free ram:")); NeoSerial.println((int) SP - (int) (__brkval == 0 ? (int)&__heap_start : (int)__brkval)); } +/// Print total SRAM size on NeoSerial console. +#define MEMORY_PRINT_TOTALSIZE { NeoSerial.print(F("SRAM size:")); NeoSerial.println((int) RAMEND - (int) &__data_start); } + +/// Displays the 'map' of the current state of the Arduino's SRAM memory on the NeoSerial console. +void SRamDisplay(void); + +// +// Stack count part. STACK_COMPUTE will get the maximum size of the stack at the moment... +// + +/// Must be used only one time, outside any function. +#define STACK_DECLARE unsigned int mu_stack_size = (RAMEND - 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;} + +/// Compute the current maximum and show it now with customized text. +#define STACK_PRINT_TEXT(text) { STACK_COMPUTE; NeoSerial.print(text); NeoSerial.println(mu_stack_size); } + +/// Compute the current maximum and show it now with default text. +#define STACK_PRINT STACK_PRINT_TEXT(F("Stack Maximum Size (Instrumentation method): ")); + +// +// Free Ram part. +// + +/// Shows the current free SRAM memory with customized text. +#define FREERAM_PRINT_TEXT(text) NeoSerial.print(text); NeoSerial.println(mu_freeRam()); + +/// Shows the current free SRAM memory with default text. +#define FREERAM_PRINT FREERAM_PRINT_TEXT(F("Free Ram Size: ")); + +/// Show the free Ram size on console. +int mu_freeRam(void); + +// +// StackPaint part. This macro gives a view of used stack area at the end of execution... +// + +/// Show the stack size on console. +uint16_t mu_StackCount(void); + +/// Compute the current maximum and show it now with customized text. +#define STACKPAINT_PRINT_TEXT(text) { NeoSerial.print(text); NeoSerial.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): ")); + + +#endif