diff options
| -rw-r--r-- | atmel-samd/Makefile | 7 | ||||
| -rw-r--r-- | atmel-samd/main.c | 7 | ||||
| -rw-r--r-- | atmel-samd/mpconfigport.h | 9 | ||||
| -rw-r--r-- | py/mpconfig.h | 5 | ||||
| -rw-r--r-- | py/mpstate.h | 4 | ||||
| -rw-r--r-- | py/objmodule.c | 2 | ||||
| -rw-r--r-- | py/stackctrl.c | 25 | ||||
| -rw-r--r-- | py/stackctrl.h | 8 | ||||
| -rw-r--r-- | shared-bindings/ustack/__init__.c | 88 | ||||
| -rw-r--r-- | shared-bindings/ustack/__init__.h | 38 | ||||
| -rw-r--r-- | shared-module/ustack/__init__.c | 52 |
11 files changed, 240 insertions, 5 deletions
diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index cc3e0b399..ed5981d09 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -128,10 +128,10 @@ CFLAGS_CORTEX_M0 = \ CFLAGS = $(INC) -Wall -Werror -std=gnu11 -nostdlib $(CFLAGS_CORTEX_M0) $(CFLAGS_MOD) $(COPT) #Debugging/Optimization -# TODO(tannewt): Figure out what NDEBUG does. Adding it to the debug build -# reduces code size pretty dramatically. ifeq ($(DEBUG), 1) -CFLAGS += -Os -ggdb -DNDEBUG -DENABLE_MICRO_TRACE_BUFFER +# NDEBUG disables assert() statements. This reduces code size pretty dramatically, per tannewt. +# Turn on Python modules useful for debugging (e.g. uheap, ustack). +CFLAGS += -Os -ggdb -DNDEBUG -DENABLE_MICRO_TRACE_BUFFER -DMICROPY_DEBUG_MODULES else CFLAGS += -Os -DNDEBUG -flto endif @@ -279,6 +279,7 @@ SRC_SHARED_MODULE = \ random/__init__.c \ storage/__init__.c \ uheap/__init__.c \ + ustack/__init__.c SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ $(addprefix shared-module/, $(SRC_SHARED_MODULE)) diff --git a/atmel-samd/main.c b/atmel-samd/main.c index 64b7d8c7f..842dae451 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -581,6 +581,13 @@ int main(void) { mp_stack_ctrl_init(); mp_stack_set_limit((char*)&_estack - (char*)&_ebss - 1024); + +#if MICROPY_MAX_STACK_USAGE + // _ezero (same as _ebss) is an int, so start 4 bytes above it. + mp_stack_set_bottom(&_ezero + 1); + mp_stack_fill_with_sentinel(); +#endif + init_flash_fs(); // Reset everything and prep MicroPython to run boot.py. diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index dd62a9f05..7755959ea 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -105,6 +105,11 @@ #define MICROPY_STACK_CHECK (1) +// Track stack usage on a debug build. Expose results via ustack module. +#ifdef MICROPY_DEBUG_MODULES +#define MICROPY_MAX_STACK_USAGE (1) +#endif + // This port is intended to be 32-bit, but unfortunately, int32_t for // different targets may be defined in different ways - either as int // or as long. This requires different printf formatting specifiers @@ -145,6 +150,7 @@ extern const struct _mp_obj_module_t storage_module; extern const struct _mp_obj_module_t time_module; extern const struct _mp_obj_module_t neopixel_write_module; extern const struct _mp_obj_module_t uheap_module; +extern const struct _mp_obj_module_t ustack_module; extern const struct _mp_obj_module_t samd_module; extern const struct _mp_obj_module_t touchio_module; extern const struct _mp_obj_module_t usb_hid_module; @@ -188,7 +194,8 @@ extern const struct _mp_obj_module_t usb_hid_module; EXTRA_BUILTIN_MODULES #define MICROPY_PORT_BUILTIN_DEBUG_MODULES \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module } + { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_ustack),(mp_obj_t)&ustack_module } #ifndef MICROPY_PIN_DEFS_PORT_H #define MICROPY_PIN_DEFS_PORT_H "pins.h" diff --git a/py/mpconfig.h b/py/mpconfig.h index 39e773183..14c5af3e7 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -446,6 +446,11 @@ #define MICROPY_STACK_CHECK (0) #endif +// Whether to measure maximum stack excursion +#ifndef MICROPY_MAX_STACK_USAGE +#define MICROPY_MAX_STACK_USAGE (0) +#endif + // Whether to have an emergency exception buffer #ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) diff --git a/py/mpstate.h b/py/mpstate.h index 30785775b..0b195a096 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -221,6 +221,10 @@ typedef struct _mp_state_thread_t { // Stack top at the start of program char *stack_top; + #if MICROPY_MAX_STACK_USAGE + char* stack_bottom; + #endif + #if MICROPY_STACK_CHECK size_t stack_limit; #endif diff --git a/py/objmodule.c b/py/objmodule.c index 9171b2b22..1b9b9d65e 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -226,7 +226,7 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { // extra builtin modules as defined by a port MICROPY_PORT_BUILTIN_MODULES -#if defined(DEBUG) && defined(MICROPY_PORT_BUILTIN_DEBUG_MODULES) +#if defined(MICROPY_DEBUG_MODULES) && defined(MICROPY_PORT_BUILTIN_DEBUG_MODULES) , MICROPY_PORT_BUILTIN_DEBUG_MODULES #endif }; diff --git a/py/stackctrl.c b/py/stackctrl.c index d9969b56c..202ae3d48 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -66,3 +66,28 @@ void mp_stack_check(void) { } #endif // MICROPY_STACK_CHECK + +#if MICROPY_MAX_STACK_USAGE + +// Fill stack space with this unusual value. +const char MP_MAX_STACK_USAGE_SENTINEL_BYTE = 0xEE; + +// Record absolute bottom (logical limit) of stack. +void mp_stack_set_bottom(void* stack_bottom) { + MP_STATE_THREAD(stack_bottom) = stack_bottom; +} + +// Fill stack space down toward the stack limit with a known unusual value. +void mp_stack_fill_with_sentinel(void) { + // Force routine to not be inlined. Better guarantee than MP_NOINLINE for -flto. + __asm volatile (""); + volatile char* volatile p; + // Start filling stack just below the last variable in the current stack frame, which is p. + // Continue until we've hit the bottom of the stack (lowest address, logical "ceiling" of stack). + p = (char *) (&p - 1); + while(p >= MP_STATE_THREAD(stack_bottom)) { + *p-- = MP_MAX_STACK_USAGE_SENTINEL_BYTE; + } +} + +#endif // MICROPY_MAX_STACK_USAGE diff --git a/py/stackctrl.h b/py/stackctrl.h index e915f5000..2fb3784c5 100644 --- a/py/stackctrl.h +++ b/py/stackctrl.h @@ -45,4 +45,12 @@ void mp_stack_check(void); #endif +#if MICROPY_MAX_STACK_USAGE + +const char MP_MAX_STACK_USAGE_SENTINEL_BYTE; +void mp_stack_set_bottom(void* stack_bottom); +void mp_stack_fill_with_sentinel(void); + +#endif + #endif // __MICROPY_INCLUDED_PY_STACKCTRL_H__ diff --git a/shared-bindings/ustack/__init__.c b/shared-bindings/ustack/__init__.c new file mode 100644 index 000000000..c4391c132 --- /dev/null +++ b/shared-bindings/ustack/__init__.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/ustack/__init__.h" + +//| :mod:`ustack` --- Stack information and analysis +//| ======================================================== +//| +//| .. module:: ustack +//| :synopsis: stack information functions +//| + +#if MICROPY_MAX_STACK_USAGE +//| .. method:: max_stack_usage() +//| +//| Return the maximum excursion of the stack so far. +//| +STATIC mp_obj_t max_stack_usage(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_max_stack_usage()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(max_stack_usage_obj, max_stack_usage); + +#endif // MICROPY_MAX_STACK_USAGE + +//| .. method:: stack_size() +//| +//| Return the size of the entire stack. +//| Same as in micropython.mem_info(), but returns a value instead +//| of just printing it. +//| +STATIC mp_obj_t stack_size(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_size()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_size_obj, stack_size); + +//| .. method:: stack_usage() +//| +//| Return how much stack is currently in use. +//| Same as micropython.stack_use(); duplicated here for convenience. +//| +STATIC mp_obj_t stack_usage(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_usage()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_usage_obj, stack_usage); + +STATIC const mp_rom_map_elem_t ustack_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustack) }, + #if MICROPY_MAX_STACK_USAGE + { MP_ROM_QSTR(MP_QSTR_max_stack_usage), MP_ROM_PTR(&max_stack_usage_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&stack_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_stack_usage), MP_ROM_PTR(&stack_usage_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ustack_module_globals, ustack_module_globals_table); + +const mp_obj_module_t ustack_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&ustack_module_globals, +}; diff --git a/shared-bindings/ustack/__init__.h b/shared-bindings/ustack/__init__.h new file mode 100644 index 000000000..d4f3d50a8 --- /dev/null +++ b/shared-bindings/ustack/__init__.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__ +#define __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__ + +#include "py/obj.h" + +#if MICROPY_MAX_STACK_USAGE +extern uint32_t shared_module_ustack_max_stack_usage(void); +#endif +extern uint32_t shared_module_ustack_stack_size(void); +extern uint32_t shared_module_ustack_stack_usage(void); + +#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__ diff --git a/shared-module/ustack/__init__.c b/shared-module/ustack/__init__.c new file mode 100644 index 000000000..947f81f8e --- /dev/null +++ b/shared-module/ustack/__init__.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> + +#include "py/mpstate.h" +#include "py/stackctrl.h" + +#include "shared-bindings/ustack/__init__.h" + +#if MICROPY_MAX_STACK_USAGE +uint32_t shared_module_ustack_max_stack_usage(void) { + // Start at stack limit and move up. + // Untouched stack was filled with a sentinel value. + // Stop at first non-sentinel byte. + char* p = MP_STATE_THREAD(stack_bottom); + while (*p++ == MP_MAX_STACK_USAGE_SENTINEL_BYTE) { } + return MP_STATE_THREAD(stack_top) - p; +} +#endif + +uint32_t shared_module_ustack_stack_size() { + return MP_STATE_THREAD(stack_limit); +} + +uint32_t shared_module_ustack_stack_usage() { + return mp_stack_usage(); +} + |
