@@ -569,7 +569,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
569
569
#endif
570
570
571
571
#if EXTENSIVE_HEAP_PROFILING
572
- gc_dump_alloc_table ();
572
+ gc_dump_alloc_table (true, true );
573
573
#endif
574
574
575
575
return ret_ptr ;
@@ -623,7 +623,7 @@ void gc_free(void *ptr) {
623
623
GC_EXIT ();
624
624
625
625
#if EXTENSIVE_HEAP_PROFILING
626
- gc_dump_alloc_table ();
626
+ gc_dump_alloc_table (true, true );
627
627
#endif
628
628
}
629
629
}
@@ -752,7 +752,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
752
752
GC_EXIT ();
753
753
754
754
#if EXTENSIVE_HEAP_PROFILING
755
- gc_dump_alloc_table ();
755
+ gc_dump_alloc_table (true, true );
756
756
#endif
757
757
758
758
return ptr_in ;
@@ -777,7 +777,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
777
777
#endif
778
778
779
779
#if EXTENSIVE_HEAP_PROFILING
780
- gc_dump_alloc_table ();
780
+ gc_dump_alloc_table (true, true );
781
781
#endif
782
782
783
783
return ptr_in ;
@@ -820,27 +820,59 @@ void gc_dump_info(void) {
820
820
(uint )info .num_1block , (uint )info .num_2block , (uint )info .max_block , (uint )info .max_free );
821
821
}
822
822
823
- void gc_dump_alloc_table (void ) {
823
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
824
+ // Power-of-two size classes. i.e. class[0] is how many single-block
825
+ // allocations are possible. class[5] is how many 32-block allocations.
826
+ #define NUM_FRAGMENTATION_CLASSES (20)
827
+
828
+ // Given a number of contiguous blocks, figure out how many allocations of
829
+ // each size class would fit in that many blocks.
830
+ STATIC void gc_update_fragmentation_stats (size_t n , size_t * frag_classes ) {
831
+ for (size_t c = 1 , i = 0 ; c < n && i < NUM_FRAGMENTATION_CLASSES ; c <<= 1 , ++ i ) {
832
+ frag_classes [i ] += n / c ;
833
+ }
834
+ }
835
+ #endif
836
+
837
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS || MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
838
+ void gc_dump_alloc_table (bool print_table , bool print_fragmentation ) {
824
839
GC_ENTER ();
840
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
825
841
static const size_t DUMP_BYTES_PER_LINE = 64 ;
842
+ #endif
826
843
#if !EXTENSIVE_HEAP_PROFILING
827
- // When comparing heap output we don't want to print the starting
828
- // pointer of the heap because it changes from run to run.
829
- mp_printf (& mp_plat_print , "GC memory layout; from %p:" , MP_STATE_MEM (gc_pool_start ));
844
+ // Skip this when EXTENSIVE_HEAP_PROFILING is on because the starting
845
+ // pointer of the heap changes from run to run.
846
+ if (print_table ) {
847
+ mp_printf (& mp_plat_print , "GC memory layout; from %p:" , MP_STATE_MEM (gc_pool_start ));
848
+ }
849
+ #endif
850
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
851
+ // How many consecutive free blocks.
852
+ size_t nfree = 0 ;
853
+ // Number of allocs that would succeed for 1, 2, 4, .. 2^n blocks.
854
+ MICROPY_GC_STACK_ENTRY_TYPE frag_classes [NUM_FRAGMENTATION_CLASSES ] = {0 };
830
855
#endif
831
856
for (size_t bl = 0 ; bl < MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; bl ++ ) {
832
- if (bl % DUMP_BYTES_PER_LINE == 0 ) {
857
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
858
+ if (print_table && bl % DUMP_BYTES_PER_LINE == 0 ) {
833
859
// a new line of blocks
834
860
{
835
861
// check if this line contains only free blocks
836
862
size_t bl2 = bl ;
837
863
while (bl2 < MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB && ATB_GET_KIND (bl2 ) == AT_FREE ) {
838
864
bl2 ++ ;
839
865
}
840
- if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE ) {
866
+ size_t skip = bl2 - bl ;
867
+ size_t lines = skip / DUMP_BYTES_PER_LINE ;
868
+ if (lines >= 2 ) {
841
869
// there are at least 2 lines containing only free blocks, so abbreviate their printing
842
- mp_printf (& mp_plat_print , "\n (%u lines all free)" , (uint )(bl2 - bl ) / DUMP_BYTES_PER_LINE );
843
- bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1 ));
870
+ mp_printf (& mp_plat_print , "\n (%u lines all free)" , (uint )lines );
871
+ skip = lines * DUMP_BYTES_PER_LINE ;
872
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
873
+ nfree += skip ;
874
+ #endif
875
+ bl += skip ;
844
876
if (bl >= MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ) {
845
877
// got to end of heap
846
878
break ;
@@ -852,13 +884,22 @@ void gc_dump_alloc_table(void) {
852
884
// mp_printf(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff));
853
885
mp_printf (& mp_plat_print , "\n%05x: " , (uint )((bl * BYTES_PER_BLOCK ) & (uint32_t )0xfffff ));
854
886
}
887
+ #endif
888
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
855
889
int c = ' ' ;
890
+ #endif
856
891
switch (ATB_GET_KIND (bl )) {
857
892
case AT_FREE :
893
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
858
894
c = '.' ;
895
+ #endif
896
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
897
+ ++ nfree ;
898
+ #endif
859
899
break ;
860
900
/* this prints out if the object is reachable from BSS or STACK (for unix only)
861
901
case AT_HEAD: {
902
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
862
903
c = 'h';
863
904
void **ptrs = (void**)(void*)&mp_state_ctx;
864
905
mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t);
@@ -880,11 +921,13 @@ void gc_dump_alloc_table(void) {
880
921
}
881
922
}
882
923
}
883
- break;
924
+ #endif
925
+ goto reset_frag;
884
926
}
885
927
*/
886
928
/* this prints the uPy object type of the head block */
887
929
case AT_HEAD : {
930
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
888
931
void * * ptr = (void * * )(MP_STATE_MEM (gc_pool_start ) + bl * BYTES_PER_BLOCK );
889
932
if (* ptr == & mp_type_tuple ) {
890
933
c = 'T' ;
@@ -934,20 +977,47 @@ void gc_dump_alloc_table(void) {
934
977
}
935
978
#endif
936
979
}
937
- break ;
980
+ #endif
981
+ goto reset_frag ;
938
982
}
939
983
case AT_TAIL :
984
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
940
985
c = '=' ;
941
- break ;
986
+ #endif
987
+ goto reset_frag ;
942
988
case AT_MARK :
989
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
943
990
c = 'm' ;
991
+ #endif
992
+ goto reset_frag ;
993
+ reset_frag :
994
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
995
+ gc_update_fragmentation_stats (nfree , frag_classes );
996
+ nfree = 0 ;
997
+ #endif
944
998
break ;
945
999
}
946
- mp_printf (& mp_plat_print , "%c" , c );
1000
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
1001
+ if (print_table ) {
1002
+ mp_printf (& mp_plat_print , "%c" , c );
1003
+ }
1004
+ #endif
947
1005
}
1006
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
1007
+ if (print_fragmentation ) {
1008
+ gc_update_fragmentation_stats (nfree , frag_classes );
1009
+ mp_print_str (& mp_plat_print , "Frag:" );
1010
+ size_t c = 1 ;
1011
+ for (int i = 0 ; i < NUM_FRAGMENTATION_CLASSES ; ++ i ) {
1012
+ mp_printf (& mp_plat_print , " %u: %u," , c , frag_classes [i ]);
1013
+ c <<= 1 ;
1014
+ }
1015
+ }
1016
+ #endif
948
1017
mp_print_str (& mp_plat_print , "\n" );
949
1018
GC_EXIT ();
950
1019
}
1020
+ #endif
951
1021
952
1022
#if 0
953
1023
// For testing the GC functions
0 commit comments