diff --git a/cpu/atmega_common/cpu.c b/cpu/atmega_common/cpu.c index 01f1c4cfc0..f8978d86b0 100644 --- a/cpu/atmega_common/cpu.c +++ b/cpu/atmega_common/cpu.c @@ -116,6 +116,28 @@ void cpu_init(void) periph_init(); } +struct __freelist { + size_t size; + struct __freelist *next; +}; + +extern struct __freelist *__flp; +extern char *__malloc_heap_start; +extern char *__malloc_heap_end; +extern char *__brkval; + +void heap_stats(void) +{ + int heap_size = __malloc_heap_end - __malloc_heap_start; + int free = __malloc_heap_end - __brkval; + struct __freelist *fp; + for (fp = __flp; fp; fp = fp->next) { + free += fp->size; + } + printf("heap: %d (used %d, free %d) [bytes]\n", + heap_size, heap_size - free, free); +} + /* This is a vector which is aliased to __vector_default, * the vector executed when an ISR fires with no accompanying * ISR handler. This may be used along with the ISR() macro to diff --git a/cpu/atmega_common/include/cpu_conf.h b/cpu/atmega_common/include/cpu_conf.h index 7eacb5bf2b..37ede8582f 100644 --- a/cpu/atmega_common/include/cpu_conf.h +++ b/cpu/atmega_common/include/cpu_conf.h @@ -56,6 +56,11 @@ extern "C" { */ #define PUF_SRAM_ATTRIBUTES __attribute__((used, section(".noinit"))) +/** + * @brief Declare the heap_stats function as available + */ +#define HAVE_HEAP_STATS + #ifdef __cplusplus } #endif diff --git a/cpu/esp32/include/cpu_conf.h b/cpu/esp32/include/cpu_conf.h index 3d04a391c7..2668bf7fec 100644 --- a/cpu/esp32/include/cpu_conf.h +++ b/cpu/esp32/include/cpu_conf.h @@ -31,6 +31,11 @@ extern "C" { #endif +/** + * @brief Declare the heap_stats function as available + */ +#define HAVE_HEAP_STATS + /** * @name Stack size configuration * @{ diff --git a/cpu/esp32/syscalls.c b/cpu/esp32/syscalls.c index 4691564b83..417d53ce1c 100644 --- a/cpu/esp32/syscalls.c +++ b/cpu/esp32/syscalls.c @@ -60,6 +60,9 @@ #ifdef MODULE_ESP_IDF_HEAP #include "heap/esp_heap_caps.h" +#include "heap/include/multi_heap.h" +#else +#include "malloc.h" #endif #define MHZ 1000000UL @@ -218,6 +221,24 @@ void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size) return result; } +unsigned int IRAM get_free_heap_size (void) +{ + return heap_caps_get_free_size( MALLOC_CAP_DEFAULT ); +} + +void heap_stats(void) +{ + multi_heap_info_t hinfo; + + heap_caps_get_info(&hinfo, MALLOC_CAP_DEFAULT); + + size_t _free = hinfo.total_free_bytes; + size_t _alloc = hinfo.total_allocated_bytes; + + printf("heap: %u (used %u free %u) [bytes]\n", + (unsigned)(_free + _alloc), (unsigned)_alloc, (unsigned)_free); +} + #else /* MODULE_ESP_IDF_HEAP */ /* for compatibiliy with ESP-IDF heap functions */ @@ -242,21 +263,23 @@ extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ extern uint8_t *heap_top; /* current top of heap as defined in newlib_syscalls_default */ -#endif /* MODULE_ESP_IDF_HEAP */ - unsigned int IRAM get_free_heap_size (void) { - #if MODULE_ESP_IDF_HEAP - return heap_caps_get_free_size( MALLOC_CAP_DEFAULT ); - #else - return &_eheap - ((heap_top) ? heap_top : &_sheap); - #endif + struct mallinfo minfo = mallinfo(); + return &_eheap - &_sheap - minfo.uordblks; } +void heap_stats(void) +{ + printf("heap: %u (used %u free %u)\n", (unsigned)(&_eheap - &_sheap), + &_eheap - &_sheap - get_free_heap_size(), get_free_heap_size()); +} + +#endif /* MODULE_ESP_IDF_HEAP */ + /* alias for compatibility with espressif/wifi_libs */ uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); - /** * @name Other system functions */ diff --git a/cpu/esp8266/Makefile.dep b/cpu/esp8266/Makefile.dep index f817b2f6ff..9cd556e24f 100644 --- a/cpu/esp8266/Makefile.dep +++ b/cpu/esp8266/Makefile.dep @@ -10,7 +10,6 @@ ifneq (, $(filter esp_sdk, $(USEMODULE))) LINKFLAGS += -Wl,-wrap=_malloc_r LINKFLAGS += -Wl,-wrap=_free_r LINKFLAGS += -Wl,-wrap=_realloc_r - LINKFLAGS += -Wl,-wrap=mallinfo endif ifneq (, $(filter esp_spiffs, $(USEMODULE))) diff --git a/cpu/esp8266/syscalls.c b/cpu/esp8266/syscalls.c index c903ea899a..a3b99087c9 100644 --- a/cpu/esp8266/syscalls.c +++ b/cpu/esp8266/syscalls.c @@ -74,7 +74,9 @@ void *__real_realloc(void *ptr, size_t size); void *__real__malloc_r (struct _reent *r, size_t size); void __real__free_r (struct _reent *r, void *ptr); void *__real__realloc_r (struct _reent *r, void *ptr, size_t size); -struct mallinfo __real_mallinfo(void); + +extern uint8_t _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */ +extern uint8_t _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */ void* IRAM __wrap_malloc(size_t size) { @@ -136,27 +138,6 @@ unsigned int get_free_heap_size (void) return xPortGetFreeHeapSize(); } -extern uint8_t _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */ -extern uint8_t _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */ - -struct mallinfo __wrap_mallinfo(void) -{ - struct mallinfo mi; - - mi.arena = &_eheap - &_sheap; - mi.fordblks = get_free_heap_size(); - mi.uordblks = mi.arena - mi.fordblks; - mi.keepcost = mi.fordblks; - return mi; -} - -void heap_stats(void) -{ - struct mallinfo minfo = __wrap_mallinfo(); - ets_printf("heap: %d (used %d, free %d)\n", - minfo.arena, minfo.uordblks, minfo.fordblks); -} - void IRAM syscalls_init (void) { } @@ -346,20 +327,20 @@ extern char _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */ extern char _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */ unsigned int IRAM get_free_heap_size (void) -{ - return (_cheap) ? &_eheap - _cheap : 0; -} - -void heap_stats(void) { struct mallinfo minfo = mallinfo(); - ets_printf("heap: %u (free %u), ", &_eheap - &_sheap, get_free_heap_size()); - ets_printf("sysmem: %d (used %d, free %d)\n", - minfo.arena, minfo.uordblks, minfo.fordblks); + return &_eheap - &_sheap - minfo.uordblks; } #endif /* MODULE_ESP_SDK */ +void heap_stats(void) +{ + ets_printf("heap: %u (used %d, free %u) [bytes]\n", + &_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(), + get_free_heap_size()); +} + int _rename_r (struct _reent *r, const char* old, const char* new) { DEBUG("%s: system function does not exist\n", __func__); diff --git a/cpu/fe310/cpu.c b/cpu/fe310/cpu.c index 77f5c5f106..c7a7e7c848 100644 --- a/cpu/fe310/cpu.c +++ b/cpu/fe310/cpu.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "thread.h" #include "irq.h" @@ -378,3 +379,17 @@ void thread_yield_higher(void) /* Latency of SW intr can be 4-7 cycles; wait for the SW intr */ __asm__ volatile ("wfi"); } + +/** + * @brief Print heap statistics + */ +void heap_stats(void) +{ + extern char _heap_start; /* defined in linker script */ + extern char _heap_end; /* defined in linker script */ + + long int heap_size = &_heap_end - &_heap_start; + struct mallinfo minfo = mallinfo(); + printf("heap: %ld (used %u, free %ld) [bytes]\n", + heap_size, minfo.uordblks, heap_size - minfo.uordblks); +} diff --git a/cpu/fe310/include/cpu_conf.h b/cpu/fe310/include/cpu_conf.h index 4023b00798..698ea7873a 100644 --- a/cpu/fe310/include/cpu_conf.h +++ b/cpu/fe310/include/cpu_conf.h @@ -34,6 +34,11 @@ #endif /** @} */ +/** + * @brief Declare the heap_stats function as available + */ +#define HAVE_HEAP_STATS + #ifdef __cplusplus extern "C" { #endif diff --git a/cpu/mips32r2_common/Makefile.include b/cpu/mips32r2_common/Makefile.include index 00bf930738..79c3f89cde 100644 --- a/cpu/mips32r2_common/Makefile.include +++ b/cpu/mips32r2_common/Makefile.include @@ -11,6 +11,7 @@ ifeq ($(USE_UHI_SYSCALLS),1) #Use UHI to handle syscalls export LINKFLAGS += -luhi export USEMODULE += newlib_syscalls_mips_uhi + CFLAGS += -DHAVE_HEAP_STATS else #Use RIOT to handle syscalls (default) export USEMODULE += newlib_syscalls_default diff --git a/cpu/mips32r2_common/cpu.c b/cpu/mips32r2_common/cpu.c index 5f3d53bc45..b012b751f4 100644 --- a/cpu/mips32r2_common/cpu.c +++ b/cpu/mips32r2_common/cpu.c @@ -7,11 +7,13 @@ * directory for more details. */ +#include #include #include #include #include #include +#include #include "periph/uart.h" #include "periph/timer.h" @@ -83,3 +85,35 @@ void cpu_init(void) /* trigger static peripheral initialization */ periph_init(); } + +#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT + +void heap_stats(void) +{ + puts("heap statistics are not supported for newlib_syscalls_default"); +} + +#else + +extern char _end[]; /* defined in linker script */ + +void heap_stats(void) +{ + void *ram_base; + void *ram_extent; + unsigned long heap_size; + + _get_ram_range (&ram_base, &ram_extent); + /* If the _end symbol is within the RAM then use _end. */ + if ((void*)_end > ram_base && (void*)_end < ram_extent) { + heap_size = ram_extent - (void*)_end; + } + else { + heap_size = ram_extent - ram_base; + } + struct mallinfo minfo = mallinfo(); + printf("heap: %lu (used %lu, free %lu) [bytes]\n", + heap_size, minfo.uordblks, heap_size - minfo.uordblks); +} + +#endif diff --git a/cpu/msp430_common/include/cpu_conf.h b/cpu/msp430_common/include/cpu_conf.h index 09e0aa9ba4..d4fbbaeea5 100644 --- a/cpu/msp430_common/include/cpu_conf.h +++ b/cpu/msp430_common/include/cpu_conf.h @@ -77,6 +77,11 @@ extern "C" { #endif /** @} */ +/** + * @brief Declare the heap_stats function as available + */ +#define HAVE_HEAP_STATS + #ifdef __cplusplus } #endif diff --git a/cpu/msp430_common/malloc.c b/cpu/msp430_common/malloc.c index 354d94ec02..bae6ce9db4 100644 --- a/cpu/msp430_common/malloc.c +++ b/cpu/msp430_common/malloc.c @@ -224,6 +224,10 @@ free(void *p) state = irq_disable(); + if (__brkval == NULL) { + __brkval = __malloc_heap_start; + } + /* ISO C says free(NULL) must be a no-op */ if (p == NULL) { irq_restore(state); @@ -457,4 +461,26 @@ calloc(size_t nele, size_t size) return p; } +void heap_stats(void) +{ + if (__brkval == NULL) { + __brkval = __malloc_heap_start; + } + + long int heap_size = __malloc_heap_end - __malloc_heap_start; + long int free = __malloc_heap_end - __brkval; + struct __freelist *fp; + for (fp = __flp; fp; fp = fp->nx) { + free += fp->sz; + } + printf("heap: %ld (used %ld, free %ld) [bytes]\n", + heap_size, heap_size - free, free); +} + +#else + +void heap_stats(void) { + puts("heap statistics are not supported"); +} + #endif /* MODULE_MSP430_MALLOC */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index cac0eea00d..55bad5757b 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -41,6 +41,7 @@ PSEUDOMODULES += gnrc_sixlowpan_router_default PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += gnrc_txtsnd PSEUDOMODULES += i2c_scan +PSEUDOMODULES += heap_cmd PSEUDOMODULES += l2filter_blacklist PSEUDOMODULES += l2filter_whitelist PSEUDOMODULES += lis2dh12_i2c diff --git a/sys/newlib_syscalls_default/syscalls.c b/sys/newlib_syscalls_default/syscalls.c index b173233de5..de58065520 100644 --- a/sys/newlib_syscalls_default/syscalls.c +++ b/sys/newlib_syscalls_default/syscalls.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,24 @@ void *_sbrk_r(struct _reent *r, ptrdiff_t incr) return res; } +/** + * @brief Print heap statistics + * + * If the CPU does not provide its own heap handling and heap_stats function, + * but instead uses the newlib_syscall_default function, this function outputs + * the heap statistics. If the CPU provides its own heap_stats function, it + * should define HAVE_HEAP_STATS in its cpu_conf.h file. + */ +#ifndef HAVE_HEAP_STATS +__attribute__((weak)) void heap_stats(void) +{ + struct mallinfo minfo = mallinfo(); + long int heap_size = &_eheap - &_sheap; + printf("heap: %ld (used %d, free %ld) [bytes]\n", + heap_size, minfo.uordblks, heap_size - minfo.uordblks); +} +#endif /* HAVE_HEAP_STATS */ + #endif /*__mips__*/ /** diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 05fabdbf68..5688c51885 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -11,6 +11,9 @@ endif ifneq (,$(filter ps,$(USEMODULE))) SRC += sc_ps.c endif +ifneq (,$(filter heap_cmd,$(USEMODULE))) + SRC += sc_heap.c +endif ifneq (,$(filter sht1x,$(USEMODULE))) SRC += sc_sht1x.c endif diff --git a/sys/shell/commands/sc_heap.c b/sys/shell/commands/sc_heap.c index 17fe27844f..fbbdfe1847 100644 --- a/sys/shell/commands/sc_heap.c +++ b/sys/shell/commands/sc_heap.c @@ -18,14 +18,24 @@ * @} */ +#include "cpu_conf.h" + +#if defined(MODULE_NEWLIB_SYSCALLS_DEFAULT) || defined (HAVE_HEAP_STATS) extern void heap_stats(void); +#else +#include +#endif int _heap_handler(int argc, char **argv) { (void) argc; (void) argv; +#if defined(MODULE_NEWLIB_SYSCALLS_DEFAULT) || defined (HAVE_HEAP_STATS) heap_stats(); - return 0; +#else + printf("heap statistics are not supported for %s cpu\n", RIOT_CPU); + return 1; +#endif } diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index 78e703f6dd..85a314575f 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -29,7 +29,7 @@ extern int _reboot_handler(int argc, char **argv); extern int _id_handler(int argc, char **argv); #endif -#ifdef MODULE_LPC_COMMON +#ifdef MODULE_HEAP_CMD extern int _heap_handler(int argc, char **argv); #endif @@ -168,8 +168,8 @@ const shell_command_t _shell_command_list[] = { #ifdef MODULE_CONFIG {"id", "Gets or sets the node's id.", _id_handler}, #endif -#ifdef MODULE_LPC_COMMON - {"heap", "Shows the heap state for the LPC2387 on the command shell.", _heap_handler}, +#ifdef MODULE_HEAP_CMD + {"heap", "Prints heap statistics.", _heap_handler}, #endif #ifdef MODULE_PS {"ps", "Prints information about running threads.", _ps_handler}, diff --git a/tests/heap_cmd/Makefile b/tests/heap_cmd/Makefile new file mode 100644 index 0000000000..978d914bd7 --- /dev/null +++ b/tests/heap_cmd/Makefile @@ -0,0 +1,10 @@ +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-uno arduino-nano + +USEMODULE += heap_cmd +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +include $(RIOTBASE)/Makefile.include diff --git a/tests/heap_cmd/README.md b/tests/heap_cmd/README.md new file mode 100644 index 0000000000..7b29b752a8 --- /dev/null +++ b/tests/heap_cmd/README.md @@ -0,0 +1,5 @@ +# heap_cmd application + +Shell-based test application for the heap functions `malloc`, `free` +and `heap_stats`. Use the `help` command to get more information on how to use +it. diff --git a/tests/heap_cmd/main.c b/tests/heap_cmd/main.c new file mode 100644 index 0000000000..7e7fd50d0b --- /dev/null +++ b/tests/heap_cmd/main.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * @brief Shell-based test application for heap functions + * + * @author Gunar Schorcht + * + */ + +#include +#include +#include + +#include "shell_commands.h" +#include "shell.h" + +static int malloc_cmd(int argc, char **argv) +{ + static void *ptr = 0; + if (argc < 2) { + printf("usage: %s \n", argv[0]); + return 1; + } + size_t size = atoi(argv[1]); + ptr = malloc(size); + printf("allocated %p\n", ptr); + return 0; +} + +static int free_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s returned from malloc, e.g., 0x1234\n", + argv[0]); + return 1; + } + + unsigned int p = strtoul(argv[1], NULL, 16); + void *ptr = (void *)p; + free(ptr); + printf("freed %p\n", ptr); + return 0; +} + +static const shell_command_t shell_commands[] = { + { "malloc", "malloc ", malloc_cmd }, + { "free", "free returned from malloc, e.g., 0x1234", free_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("Shell-based test application for heap functions.\n" + "Use the 'help' command to get more information on how to use it."); + + /* define buffer to be used by the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + /* define own shell commands */ + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/heap_cmd/tests/01-run.py b/tests/heap_cmd/tests/01-run.py new file mode 100755 index 0000000000..7758d42f5e --- /dev/null +++ b/tests/heap_cmd/tests/01-run.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Gunar Schorcht +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + # check startup message + child.expect('Shell-based test application for heap functions.') + child.sendline('heap') + ret = child.expect(['heap: \d+ \(used \d+, free \d+\) \[bytes\]', 'heap statistics are not supported']) + if ret == 1: + return + child.sendline('malloc 100') + child.expect('allocated 0x') + addr = child.readline() + addr = addr[:-2] + child.expect_exact('> ') + child.sendline('heap') + child.expect('heap: \d+ \(used \d+, free \d+\) \[bytes\]') + child.sendline('free 0x' + addr) + child.expect('freed 0x' + addr) + child.expect_exact('>') + child.sendline('heap') + child.expect('heap: \d+ \(used \d+, free \d+\) \[bytes\]') + + +if __name__ == "__main__": + sys.exit(run(testfunc))