mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
metric for heap fragmentation (#5090)
* +Esp.getHeapUnfragness() * only in debug mode * default value * always enable, 64->32, light 32 integer square root, comments * fix when debugging is disabled * give credits * cosmetics * fragmentation metric updates (doc, better api, added getMaxFreeBlockSize()) * api reworked, +example * fixe types, fix names * coding style fix * use astyle for example
This commit is contained in:
parent
bbaea5a358
commit
ce28a76a24
48
cores/esp8266/Esp-frag.cpp
Normal file
48
cores/esp8266/Esp-frag.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Esp.cpp - ESP8266-specific APIs
|
||||||
|
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "umm_malloc/umm_malloc.h"
|
||||||
|
#include "umm_malloc/umm_malloc_cfg.h"
|
||||||
|
#include "coredecls.h"
|
||||||
|
#include "Esp.h"
|
||||||
|
|
||||||
|
void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag)
|
||||||
|
{
|
||||||
|
// L2 / Euclidian norm of free block sizes.
|
||||||
|
// Having getFreeHeap()=sum(hole-size), fragmentation is given by
|
||||||
|
// 100 * (1 - sqrt(sum(hole-size²)) / sum(hole-size))
|
||||||
|
|
||||||
|
umm_info(NULL, 0);
|
||||||
|
uint8_t block_size = umm_block_size();
|
||||||
|
uint32_t fh = ummHeapInfo.freeBlocks * block_size;
|
||||||
|
if (hfree)
|
||||||
|
*hfree = fh;
|
||||||
|
if (hmax)
|
||||||
|
*hmax = ummHeapInfo.maxFreeContiguousBlocks * block_size;
|
||||||
|
if (hfrag)
|
||||||
|
*hfrag = 100 - (sqrt32(ummHeapInfo.freeSize2) * 100) / fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EspClass::getHeapFragmentation()
|
||||||
|
{
|
||||||
|
uint8_t hfrag;
|
||||||
|
getHeapStats(nullptr, nullptr, &hfrag);
|
||||||
|
return hfrag;
|
||||||
|
}
|
@ -24,6 +24,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "MD5Builder.h"
|
#include "MD5Builder.h"
|
||||||
|
#include "umm_malloc/umm_malloc.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
@ -171,6 +172,11 @@ uint32_t EspClass::getFreeHeap(void)
|
|||||||
return system_get_free_heap_size();
|
return system_get_free_heap_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t EspClass::getMaxFreeBlockSize(void)
|
||||||
|
{
|
||||||
|
return umm_max_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t EspClass::getChipId(void)
|
uint32_t EspClass::getChipId(void)
|
||||||
{
|
{
|
||||||
return system_get_chip_id();
|
return system_get_chip_id();
|
||||||
|
@ -103,10 +103,13 @@ class EspClass {
|
|||||||
void restart();
|
void restart();
|
||||||
|
|
||||||
uint16_t getVcc();
|
uint16_t getVcc();
|
||||||
uint32_t getFreeHeap();
|
|
||||||
|
|
||||||
uint32_t getChipId();
|
uint32_t getChipId();
|
||||||
|
|
||||||
|
uint32_t getFreeHeap();
|
||||||
|
uint16_t getMaxFreeBlockSize();
|
||||||
|
uint8_t getHeapFragmentation(); // in %
|
||||||
|
void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr);
|
||||||
|
|
||||||
const char * getSdkVersion();
|
const char * getSdkVersion();
|
||||||
String getCoreVersion();
|
String getCoreVersion();
|
||||||
String getFullVersion();
|
String getFullVersion();
|
||||||
|
@ -8,6 +8,7 @@ extern "C" {
|
|||||||
|
|
||||||
// TODO: put declarations here, get rid of -Wno-implicit-function-declaration
|
// TODO: put declarations here, get rid of -Wno-implicit-function-declaration
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <cont.h> // g_pcont declaration
|
#include <cont.h> // g_pcont declaration
|
||||||
|
|
||||||
extern bool timeshift64_is_set;
|
extern bool timeshift64_is_set;
|
||||||
@ -18,6 +19,8 @@ void tune_timeshift64 (uint64_t now_us);
|
|||||||
void settimeofday_cb (void (*cb)(void));
|
void settimeofday_cb (void (*cb)(void));
|
||||||
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
||||||
|
|
||||||
|
uint32_t sqrt32 (uint32_t n);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
56
cores/esp8266/sqrt32.c
Normal file
56
cores/esp8266/sqrt32.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
#include <coredecls.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t sqrt32 (uint32_t n)
|
||||||
|
{
|
||||||
|
// http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C
|
||||||
|
// Another very fast algorithm donated by Tristan.Muntsinger@gmail.com
|
||||||
|
// (note: tested across the full 32 bits range, see comment below)
|
||||||
|
|
||||||
|
// 15 iterations (c=1<<15)
|
||||||
|
|
||||||
|
unsigned int c = 0x8000;
|
||||||
|
unsigned int g = 0x8000;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if (g*g > n)
|
||||||
|
g ^= c;
|
||||||
|
c >>= 1;
|
||||||
|
if (!c)
|
||||||
|
return g;
|
||||||
|
g |= c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tested with:
|
||||||
|
*
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; ++i; )
|
||||||
|
{
|
||||||
|
uint32_t sr = sqrt32(i);
|
||||||
|
uint32_t ifsr = sqrt(i);
|
||||||
|
|
||||||
|
if (ifsr != sr)
|
||||||
|
printf("%d: i%d f%d\n", i, sr, ifsr);
|
||||||
|
|
||||||
|
if (!(i & 0xffffff))
|
||||||
|
{
|
||||||
|
printf("%i%% (0x%08x)\r", ((i >> 16) * 100) >> 16, i);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
@ -1024,6 +1024,10 @@ void ICACHE_FLASH_ATTR *umm_info( void *ptr, int force ) {
|
|||||||
if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) {
|
if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) {
|
||||||
++ummHeapInfo.freeEntries;
|
++ummHeapInfo.freeEntries;
|
||||||
ummHeapInfo.freeBlocks += curBlocks;
|
ummHeapInfo.freeBlocks += curBlocks;
|
||||||
|
ummHeapInfo.freeSize2 += (unsigned int)curBlocks
|
||||||
|
* (unsigned int)sizeof(umm_block)
|
||||||
|
* (unsigned int)curBlocks
|
||||||
|
* (unsigned int)sizeof(umm_block);
|
||||||
|
|
||||||
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
|
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
|
||||||
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
|
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
|
||||||
@ -1761,4 +1765,13 @@ size_t ICACHE_FLASH_ATTR umm_free_heap_size( void ) {
|
|||||||
return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block);
|
return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t ICACHE_FLASH_ATTR umm_max_block_size( void ) {
|
||||||
|
umm_info(NULL, 0);
|
||||||
|
return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ICACHE_FLASH_ATTR umm_block_size( void ) {
|
||||||
|
return sizeof(umm_block);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
@ -26,6 +26,8 @@ typedef struct UMM_HEAP_INFO_t {
|
|||||||
unsigned short int freeBlocks;
|
unsigned short int freeBlocks;
|
||||||
|
|
||||||
unsigned short int maxFreeContiguousBlocks;
|
unsigned short int maxFreeContiguousBlocks;
|
||||||
|
|
||||||
|
unsigned int freeSize2;
|
||||||
}
|
}
|
||||||
UMM_HEAP_INFO;
|
UMM_HEAP_INFO;
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ void *umm_realloc( void *ptr, size_t size );
|
|||||||
void umm_free( void *ptr );
|
void umm_free( void *ptr );
|
||||||
|
|
||||||
size_t umm_free_heap_size( void );
|
size_t umm_free_heap_size( void );
|
||||||
|
size_t umm_max_block_size( void );
|
||||||
|
size_t umm_block_size( void );
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -291,9 +291,12 @@ Memory, memory, memory
|
|||||||
rely on exceptions for error handling, which is not available for the ESP, and in any
|
rely on exceptions for error handling, which is not available for the ESP, and in any
|
||||||
case there is no access to the underlying code.
|
case there is no access to the underlying code.
|
||||||
|
|
||||||
Instrumenting the code with the OOM debug option and calls to ``ESP.getFreeHeap()`` will
|
Instrumenting the code with the OOM debug option and calls to
|
||||||
help the process of finding leaks. Now is time to re-read about the
|
``ESP.getFreeHeap()`` / ``ESP.getHeapFragmentation()`` /
|
||||||
`exception decoder <#exception-decoder>`__.
|
``ESP.getMaxFreeBlockSize()`` will help the process of finding memory issues.
|
||||||
|
|
||||||
|
Now is time to re-read about the `exception decoder
|
||||||
|
<#exception-decoder>`__.
|
||||||
|
|
||||||
|
|
||||||
*Some techniques for reducing memory usage*
|
*Some techniques for reducing memory usage*
|
||||||
|
@ -83,6 +83,10 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab
|
|||||||
|
|
||||||
``ESP.getFreeHeap()`` returns the free heap size.
|
``ESP.getFreeHeap()`` returns the free heap size.
|
||||||
|
|
||||||
|
``ESP.getHeapFragmentation()`` returns the fragmentation metric (0% is clean, more than ~50% is not harmless)
|
||||||
|
|
||||||
|
``ESP.getMaxFreeBlockSize()`` returns the maximum allocatable ram block regarding heap fragmentation
|
||||||
|
|
||||||
``ESP.getChipId()`` returns the ESP8266 chip ID as a 32-bit integer.
|
``ESP.getChipId()`` returns the ESP8266 chip ID as a 32-bit integer.
|
||||||
|
|
||||||
``ESP.getCoreVersion()`` returns a String containing the core version.
|
``ESP.getCoreVersion()`` returns a String containing the core version.
|
||||||
|
79
libraries/esp8266/examples/HeapMetric/HeapMetric.ino
Normal file
79
libraries/esp8266/examples/HeapMetric/HeapMetric.ino
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
// nothing else than showing heap metric usage
|
||||||
|
// released to public domain
|
||||||
|
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
void stats(const char* what) {
|
||||||
|
// we could use getFreeHeap() getMaxFreeBlockSize() and getHeapFragmentation()
|
||||||
|
// or all at once:
|
||||||
|
uint32_t free;
|
||||||
|
uint16_t max;
|
||||||
|
uint8_t frag;
|
||||||
|
ESP.getHeapStats(&free, &max, &frag);
|
||||||
|
|
||||||
|
Serial.printf("free: %5d - max: %5d - frag: %3d%% <- ", free, max, frag);
|
||||||
|
// %s requires a malloc that could fail, using println instead:
|
||||||
|
Serial.println(what);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryit(int blocksize) {
|
||||||
|
void** p;
|
||||||
|
int blocks;
|
||||||
|
|
||||||
|
// heap-used ~= blocks*sizeof(void*) + blocks*blocksize
|
||||||
|
blocks = ((ESP.getMaxFreeBlockSize() / (blocksize + sizeof(void*))) + 3) & ~3; // rounded up, multiple of 4
|
||||||
|
|
||||||
|
Serial.printf("\nFilling memory with blocks of %d bytes each\n", blocksize);
|
||||||
|
stats("before");
|
||||||
|
|
||||||
|
p = (void**)malloc(sizeof(void*) * blocks);
|
||||||
|
for (int i = 0; i < blocks; i++) {
|
||||||
|
p[i] = malloc(blocksize);
|
||||||
|
}
|
||||||
|
stats("array and blocks allocation");
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks; i += 2) {
|
||||||
|
if (p[i]) {
|
||||||
|
free(p[i]);
|
||||||
|
}
|
||||||
|
p[i] = nullptr;
|
||||||
|
}
|
||||||
|
stats("freeing every other blocks");
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks; i += 4) {
|
||||||
|
if (p[i + 1]) {
|
||||||
|
free(p[i + 1]);
|
||||||
|
}
|
||||||
|
p[i + 1] = nullptr;
|
||||||
|
}
|
||||||
|
stats("freeing every other remaining blocks");
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks; i++) {
|
||||||
|
if (p[i]) {
|
||||||
|
free(p[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats("freeing array");
|
||||||
|
|
||||||
|
free(p);
|
||||||
|
stats("after");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
|
||||||
|
tryit(8000);
|
||||||
|
tryit(4000);
|
||||||
|
tryit(2000);
|
||||||
|
tryit(1000);
|
||||||
|
tryit(500);
|
||||||
|
tryit(200);
|
||||||
|
tryit(100);
|
||||||
|
tryit(50);
|
||||||
|
tryit(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
}
|
@ -29,9 +29,12 @@ reset KEYWORD2
|
|||||||
restart KEYWORD2
|
restart KEYWORD2
|
||||||
getVcc KEYWORD2
|
getVcc KEYWORD2
|
||||||
getFreeHeap KEYWORD2
|
getFreeHeap KEYWORD2
|
||||||
|
getHeapFragmentation KEYWORD2
|
||||||
|
getMaxFreeBlockSize KEYWORD2
|
||||||
getChipId KEYWORD2
|
getChipId KEYWORD2
|
||||||
getSdkVersion KEYWORD2
|
getSdkVersion KEYWORD2
|
||||||
getCoreVersion KEYWORD2
|
getCoreVersion KEYWORD2
|
||||||
|
getFullVersion KEYWORD2
|
||||||
getBootVersion KEYWORD2
|
getBootVersion KEYWORD2
|
||||||
getBootMode KEYWORD2
|
getBootMode KEYWORD2
|
||||||
getCpuFreqMHz KEYWORD2
|
getCpuFreqMHz KEYWORD2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user