Skip to content

Commit f91493c

Browse files
dhalberttannewt
authored andcommitted
Measure and report maximum stack usage. (adafruit#175)
Add max stack usage tracking, visible via debug module ustack. Add separate cpp flag for enabling modules: MICROPY_DEBUG_MODULES
1 parent af1ede9 commit f91493c

File tree

11 files changed

+240
-5
lines changed

11 files changed

+240
-5
lines changed

atmel-samd/Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ CFLAGS_CORTEX_M0 = \
128128
CFLAGS = $(INC) -Wall -Werror -std=gnu11 -nostdlib $(CFLAGS_CORTEX_M0) $(CFLAGS_MOD) $(COPT)
129129

130130
#Debugging/Optimization
131-
# TODO(tannewt): Figure out what NDEBUG does. Adding it to the debug build
132-
# reduces code size pretty dramatically.
133131
ifeq ($(DEBUG), 1)
134-
CFLAGS += -Os -ggdb -DNDEBUG -DENABLE_MICRO_TRACE_BUFFER
132+
# NDEBUG disables assert() statements. This reduces code size pretty dramatically, per tannewt.
133+
# Turn on Python modules useful for debugging (e.g. uheap, ustack).
134+
CFLAGS += -Os -ggdb -DNDEBUG -DENABLE_MICRO_TRACE_BUFFER -DMICROPY_DEBUG_MODULES
135135
else
136136
CFLAGS += -Os -DNDEBUG -flto
137137
endif
@@ -279,6 +279,7 @@ SRC_SHARED_MODULE = \
279279
random/__init__.c \
280280
storage/__init__.c \
281281
uheap/__init__.c \
282+
ustack/__init__.c
282283

283284
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
284285
$(addprefix shared-module/, $(SRC_SHARED_MODULE))

atmel-samd/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,13 @@ int main(void) {
581581
mp_stack_ctrl_init();
582582
mp_stack_set_limit((char*)&_estack - (char*)&_ebss - 1024);
583583

584+
585+
#if MICROPY_MAX_STACK_USAGE
586+
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
587+
mp_stack_set_bottom(&_ezero + 1);
588+
mp_stack_fill_with_sentinel();
589+
#endif
590+
584591
init_flash_fs();
585592

586593
// Reset everything and prep MicroPython to run boot.py.

atmel-samd/mpconfigport.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105

106106
#define MICROPY_STACK_CHECK (1)
107107

108+
// Track stack usage on a debug build. Expose results via ustack module.
109+
#ifdef MICROPY_DEBUG_MODULES
110+
#define MICROPY_MAX_STACK_USAGE (1)
111+
#endif
112+
108113
// This port is intended to be 32-bit, but unfortunately, int32_t for
109114
// different targets may be defined in different ways - either as int
110115
// or as long. This requires different printf formatting specifiers
@@ -145,6 +150,7 @@ extern const struct _mp_obj_module_t storage_module;
145150
extern const struct _mp_obj_module_t time_module;
146151
extern const struct _mp_obj_module_t neopixel_write_module;
147152
extern const struct _mp_obj_module_t uheap_module;
153+
extern const struct _mp_obj_module_t ustack_module;
148154
extern const struct _mp_obj_module_t samd_module;
149155
extern const struct _mp_obj_module_t touchio_module;
150156
extern const struct _mp_obj_module_t usb_hid_module;
@@ -188,7 +194,8 @@ extern const struct _mp_obj_module_t usb_hid_module;
188194
EXTRA_BUILTIN_MODULES
189195

190196
#define MICROPY_PORT_BUILTIN_DEBUG_MODULES \
191-
{ MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }
197+
{ MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }, \
198+
{ MP_OBJ_NEW_QSTR(MP_QSTR_ustack),(mp_obj_t)&ustack_module }
192199

193200
#ifndef MICROPY_PIN_DEFS_PORT_H
194201
#define MICROPY_PIN_DEFS_PORT_H "pins.h"

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,11 @@
446446
#define MICROPY_STACK_CHECK (0)
447447
#endif
448448

449+
// Whether to measure maximum stack excursion
450+
#ifndef MICROPY_MAX_STACK_USAGE
451+
#define MICROPY_MAX_STACK_USAGE (0)
452+
#endif
453+
449454
// Whether to have an emergency exception buffer
450455
#ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
451456
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0)

py/mpstate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ typedef struct _mp_state_thread_t {
221221
// Stack top at the start of program
222222
char *stack_top;
223223

224+
#if MICROPY_MAX_STACK_USAGE
225+
char* stack_bottom;
226+
#endif
227+
224228
#if MICROPY_STACK_CHECK
225229
size_t stack_limit;
226230
#endif

py/objmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = {
226226
// extra builtin modules as defined by a port
227227
MICROPY_PORT_BUILTIN_MODULES
228228

229-
#if defined(DEBUG) && defined(MICROPY_PORT_BUILTIN_DEBUG_MODULES)
229+
#if defined(MICROPY_DEBUG_MODULES) && defined(MICROPY_PORT_BUILTIN_DEBUG_MODULES)
230230
, MICROPY_PORT_BUILTIN_DEBUG_MODULES
231231
#endif
232232
};

py/stackctrl.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,28 @@ void mp_stack_check(void) {
6666
}
6767

6868
#endif // MICROPY_STACK_CHECK
69+
70+
#if MICROPY_MAX_STACK_USAGE
71+
72+
// Fill stack space with this unusual value.
73+
const char MP_MAX_STACK_USAGE_SENTINEL_BYTE = 0xEE;
74+
75+
// Record absolute bottom (logical limit) of stack.
76+
void mp_stack_set_bottom(void* stack_bottom) {
77+
MP_STATE_THREAD(stack_bottom) = stack_bottom;
78+
}
79+
80+
// Fill stack space down toward the stack limit with a known unusual value.
81+
void mp_stack_fill_with_sentinel(void) {
82+
// Force routine to not be inlined. Better guarantee than MP_NOINLINE for -flto.
83+
__asm volatile ("");
84+
volatile char* volatile p;
85+
// Start filling stack just below the last variable in the current stack frame, which is p.
86+
// Continue until we've hit the bottom of the stack (lowest address, logical "ceiling" of stack).
87+
p = (char *) (&p - 1);
88+
while(p >= MP_STATE_THREAD(stack_bottom)) {
89+
*p-- = MP_MAX_STACK_USAGE_SENTINEL_BYTE;
90+
}
91+
}
92+
93+
#endif // MICROPY_MAX_STACK_USAGE

py/stackctrl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,12 @@ void mp_stack_check(void);
4545

4646
#endif
4747

48+
#if MICROPY_MAX_STACK_USAGE
49+
50+
const char MP_MAX_STACK_USAGE_SENTINEL_BYTE;
51+
void mp_stack_set_bottom(void* stack_bottom);
52+
void mp_stack_fill_with_sentinel(void);
53+
54+
#endif
55+
4856
#endif // __MICROPY_INCLUDED_PY_STACKCTRL_H__

shared-bindings/ustack/__init__.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 Dan Halbert for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdint.h>
28+
29+
#include "py/obj.h"
30+
#include "py/runtime.h"
31+
32+
#include "shared-bindings/ustack/__init__.h"
33+
34+
//| :mod:`ustack` --- Stack information and analysis
35+
//| ========================================================
36+
//|
37+
//| .. module:: ustack
38+
//| :synopsis: stack information functions
39+
//|
40+
41+
#if MICROPY_MAX_STACK_USAGE
42+
//| .. method:: max_stack_usage()
43+
//|
44+
//| Return the maximum excursion of the stack so far.
45+
//|
46+
STATIC mp_obj_t max_stack_usage(void) {
47+
return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_max_stack_usage());
48+
}
49+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(max_stack_usage_obj, max_stack_usage);
50+
51+
#endif // MICROPY_MAX_STACK_USAGE
52+
53+
//| .. method:: stack_size()
54+
//|
55+
//| Return the size of the entire stack.
56+
//| Same as in micropython.mem_info(), but returns a value instead
57+
//| of just printing it.
58+
//|
59+
STATIC mp_obj_t stack_size(void) {
60+
return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_size());
61+
}
62+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_size_obj, stack_size);
63+
64+
//| .. method:: stack_usage()
65+
//|
66+
//| Return how much stack is currently in use.
67+
//| Same as micropython.stack_use(); duplicated here for convenience.
68+
//|
69+
STATIC mp_obj_t stack_usage(void) {
70+
return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_usage());
71+
}
72+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_usage_obj, stack_usage);
73+
74+
STATIC const mp_rom_map_elem_t ustack_module_globals_table[] = {
75+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustack) },
76+
#if MICROPY_MAX_STACK_USAGE
77+
{ MP_ROM_QSTR(MP_QSTR_max_stack_usage), MP_ROM_PTR(&max_stack_usage_obj) },
78+
#endif
79+
{ MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&stack_size_obj) },
80+
{ MP_ROM_QSTR(MP_QSTR_stack_usage), MP_ROM_PTR(&stack_usage_obj) },
81+
};
82+
83+
STATIC MP_DEFINE_CONST_DICT(ustack_module_globals, ustack_module_globals_table);
84+
85+
const mp_obj_module_t ustack_module = {
86+
.base = { &mp_type_module },
87+
.globals = (mp_obj_dict_t*)&ustack_module_globals,
88+
};

shared-bindings/ustack/__init__.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 Dan Halbert for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#ifndef __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__
28+
#define __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__
29+
30+
#include "py/obj.h"
31+
32+
#if MICROPY_MAX_STACK_USAGE
33+
extern uint32_t shared_module_ustack_max_stack_usage(void);
34+
#endif
35+
extern uint32_t shared_module_ustack_stack_size(void);
36+
extern uint32_t shared_module_ustack_stack_usage(void);
37+
38+
#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H__

shared-module/ustack/__init__.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 Dan Halbert
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdint.h>
28+
29+
#include "py/mpstate.h"
30+
#include "py/stackctrl.h"
31+
32+
#include "shared-bindings/ustack/__init__.h"
33+
34+
#if MICROPY_MAX_STACK_USAGE
35+
uint32_t shared_module_ustack_max_stack_usage(void) {
36+
// Start at stack limit and move up.
37+
// Untouched stack was filled with a sentinel value.
38+
// Stop at first non-sentinel byte.
39+
char* p = MP_STATE_THREAD(stack_bottom);
40+
while (*p++ == MP_MAX_STACK_USAGE_SENTINEL_BYTE) { }
41+
return MP_STATE_THREAD(stack_top) - p;
42+
}
43+
#endif
44+
45+
uint32_t shared_module_ustack_stack_size() {
46+
return MP_STATE_THREAD(stack_limit);
47+
}
48+
49+
uint32_t shared_module_ustack_stack_usage() {
50+
return mp_stack_usage();
51+
}
52+

0 commit comments

Comments
 (0)