py: Improve memory usage debugging; better GC AT dumping.
In unix port, mem_info(1) now prints pretty GC alloc table.
This commit is contained in:
parent
564963a170
commit
0b13f3e026
35
py/gc.c
35
py/gc.c
@ -492,7 +492,7 @@ void gc_free(void *ptr_in) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_uint_t gc_nbytes(void *ptr_in) {
|
mp_uint_t gc_nbytes(const void *ptr_in) {
|
||||||
mp_uint_t ptr = (mp_uint_t)ptr_in;
|
mp_uint_t ptr = (mp_uint_t)ptr_in;
|
||||||
|
|
||||||
if (VERIFY_PTR(ptr)) {
|
if (VERIFY_PTR(ptr)) {
|
||||||
@ -681,31 +681,32 @@ void gc_dump_alloc_table(void) {
|
|||||||
for (mp_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) {
|
for (mp_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) {
|
||||||
if (bl % DUMP_BYTES_PER_LINE == 0) {
|
if (bl % DUMP_BYTES_PER_LINE == 0) {
|
||||||
// a new line of blocks
|
// a new line of blocks
|
||||||
#if EXTENSIVE_HEAP_PROFILING
|
|
||||||
{
|
{
|
||||||
// check if this line contains only free blocks
|
// check if this line contains only free blocks
|
||||||
bool only_free_blocks = true;
|
mp_uint_t bl2 = bl;
|
||||||
for (mp_uint_t bl2 = bl; bl2 < gc_alloc_table_byte_len * BLOCKS_PER_ATB && bl2 < bl + DUMP_BYTES_PER_LINE; bl2++) {
|
while (bl2 < gc_alloc_table_byte_len * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) {
|
||||||
if (ATB_GET_KIND(bl2) != AT_FREE) {
|
bl2++;
|
||||||
|
}
|
||||||
only_free_blocks = false;
|
if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) {
|
||||||
|
// there are at least 2 lines containing only free blocks, so abbreviate their printing
|
||||||
|
printf("\n (" UINT_FMT " lines all free)", (bl2 - bl) / DUMP_BYTES_PER_LINE);
|
||||||
|
bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1));
|
||||||
|
if (bl >= gc_alloc_table_byte_len * BLOCKS_PER_ATB) {
|
||||||
|
// got to end of heap
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (only_free_blocks) {
|
|
||||||
// line contains only free blocks, so skip printing it
|
|
||||||
bl += DUMP_BYTES_PER_LINE - 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
// print header for new line of blocks
|
// print header for new line of blocks
|
||||||
printf("\n%04x: ", (uint)bl);
|
#if EXTENSIVE_HEAP_PROFILING
|
||||||
|
printf("\n%05x: ", (uint)(bl * BYTES_PER_BLOCK) & 0xfffff);
|
||||||
|
#else
|
||||||
|
printf("\n%05x: ", (uint)PTR_FROM_BLOCK(bl) & 0xfffff);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
int c = ' ';
|
int c = ' ';
|
||||||
switch (ATB_GET_KIND(bl)) {
|
switch (ATB_GET_KIND(bl)) {
|
||||||
case AT_FREE: c = '.'; break;
|
case AT_FREE: c = '.'; break;
|
||||||
case AT_HEAD: c = 'h'; break;
|
|
||||||
/* this prints out if the object is reachable from BSS or STACK (for unix only)
|
/* this prints out if the object is reachable from BSS or STACK (for unix only)
|
||||||
case AT_HEAD: {
|
case AT_HEAD: {
|
||||||
extern char __bss_start, _end;
|
extern char __bss_start, _end;
|
||||||
@ -734,7 +735,7 @@ void gc_dump_alloc_table(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
/* this prints the uPy object type of the head block
|
/* this prints the uPy object type of the head block */
|
||||||
case AT_HEAD: {
|
case AT_HEAD: {
|
||||||
mp_uint_t *ptr = gc_pool_start + bl * WORDS_PER_BLOCK;
|
mp_uint_t *ptr = gc_pool_start + bl * WORDS_PER_BLOCK;
|
||||||
if (*ptr == (mp_uint_t)&mp_type_tuple) { c = 'T'; }
|
if (*ptr == (mp_uint_t)&mp_type_tuple) { c = 'T'; }
|
||||||
@ -742,10 +743,10 @@ void gc_dump_alloc_table(void) {
|
|||||||
else if (*ptr == (mp_uint_t)&mp_type_dict) { c = 'D'; }
|
else if (*ptr == (mp_uint_t)&mp_type_dict) { c = 'D'; }
|
||||||
else if (*ptr == (mp_uint_t)&mp_type_float) { c = 'F'; }
|
else if (*ptr == (mp_uint_t)&mp_type_float) { c = 'F'; }
|
||||||
else if (*ptr == (mp_uint_t)&mp_type_fun_bc) { c = 'B'; }
|
else if (*ptr == (mp_uint_t)&mp_type_fun_bc) { c = 'B'; }
|
||||||
|
else if (*ptr == (mp_uint_t)&mp_type_module) { c = 'M'; }
|
||||||
else { c = 'h'; }
|
else { c = 'h'; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
case AT_TAIL: c = 't'; break;
|
case AT_TAIL: c = 't'; break;
|
||||||
case AT_MARK: c = 'm'; break;
|
case AT_MARK: c = 'm'; break;
|
||||||
}
|
}
|
||||||
|
2
py/gc.h
2
py/gc.h
@ -40,7 +40,7 @@ void gc_collect_end(void);
|
|||||||
|
|
||||||
void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser);
|
void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser);
|
||||||
void gc_free(void *ptr);
|
void gc_free(void *ptr);
|
||||||
mp_uint_t gc_nbytes(void *ptr);
|
mp_uint_t gc_nbytes(const void *ptr);
|
||||||
void *gc_realloc(void *ptr, mp_uint_t n_bytes);
|
void *gc_realloc(void *ptr, mp_uint_t n_bytes);
|
||||||
|
|
||||||
typedef struct _gc_info_t {
|
typedef struct _gc_info_t {
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "mpconfig.h"
|
#include "mpconfig.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "qstr.h"
|
#include "qstr.h"
|
||||||
|
#include "gc.h"
|
||||||
|
|
||||||
// NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings)
|
// NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings)
|
||||||
// ultimately we will replace this with a static hash table of some kind
|
// ultimately we will replace this with a static hash table of some kind
|
||||||
@ -220,9 +221,17 @@ void qstr_pool_info(mp_uint_t *n_pool, mp_uint_t *n_qstr, mp_uint_t *n_str_data_
|
|||||||
*n_pool += 1;
|
*n_pool += 1;
|
||||||
*n_qstr += pool->len;
|
*n_qstr += pool->len;
|
||||||
for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
|
for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
|
||||||
|
#if MICROPY_ENABLE_GC
|
||||||
|
*n_str_data_bytes += gc_nbytes(*q); // this counts actual bytes used in heap
|
||||||
|
#else
|
||||||
*n_str_data_bytes += Q_GET_ALLOC(*q);
|
*n_str_data_bytes += Q_GET_ALLOC(*q);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if MICROPY_ENABLE_GC
|
||||||
|
*n_total_bytes += gc_nbytes(pool); // this counts actual bytes used in heap
|
||||||
|
#else
|
||||||
*n_total_bytes += sizeof(qstr_pool_t) + sizeof(qstr) * pool->alloc;
|
*n_total_bytes += sizeof(qstr_pool_t) + sizeof(qstr) * pool->alloc;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
*n_total_bytes += *n_str_data_bytes;
|
*n_total_bytes += *n_str_data_bytes;
|
||||||
}
|
}
|
||||||
|
@ -226,16 +226,20 @@ int usage(char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MICROPY_MEM_STATS
|
#if MICROPY_MEM_STATS
|
||||||
STATIC mp_obj_t mem_info(void) {
|
STATIC mp_obj_t mem_info(mp_uint_t n_args, const mp_obj_t *args) {
|
||||||
printf("mem: total=" UINT_FMT ", current=" UINT_FMT ", peak=" UINT_FMT "\n",
|
printf("mem: total=" UINT_FMT ", current=" UINT_FMT ", peak=" UINT_FMT "\n",
|
||||||
m_get_total_bytes_allocated(), m_get_current_bytes_allocated(), m_get_peak_bytes_allocated());
|
m_get_total_bytes_allocated(), m_get_current_bytes_allocated(), m_get_peak_bytes_allocated());
|
||||||
printf("stack: " UINT_FMT "\n", mp_stack_usage());
|
printf("stack: " UINT_FMT "\n", mp_stack_usage());
|
||||||
#if MICROPY_ENABLE_GC
|
#if MICROPY_ENABLE_GC
|
||||||
gc_dump_info();
|
gc_dump_info();
|
||||||
|
if (n_args == 1) {
|
||||||
|
// arg given means dump gc allocation table
|
||||||
|
gc_dump_alloc_table();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mem_info_obj, mem_info);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mem_info_obj, 0, 1, mem_info);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
STATIC mp_obj_t qstr_info(void) {
|
STATIC mp_obj_t qstr_info(void) {
|
||||||
|
Loading…
Reference in New Issue
Block a user