@@ -553,7 +553,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
553
553
#endif
554
554
555
555
#if EXTENSIVE_HEAP_PROFILING
556
- gc_dump_alloc_table ();
556
+ gc_dump_alloc_table (true, true );
557
557
#endif
558
558
559
559
return ret_ptr ;
@@ -607,7 +607,7 @@ void gc_free(void *ptr) {
607
607
GC_EXIT ();
608
608
609
609
#if EXTENSIVE_HEAP_PROFILING
610
- gc_dump_alloc_table ();
610
+ gc_dump_alloc_table (true, true );
611
611
#endif
612
612
}
613
613
}
@@ -737,7 +737,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
737
737
GC_EXIT ();
738
738
739
739
#if EXTENSIVE_HEAP_PROFILING
740
- gc_dump_alloc_table ();
740
+ gc_dump_alloc_table (true, true );
741
741
#endif
742
742
743
743
return ptr_in ;
@@ -762,7 +762,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
762
762
#endif
763
763
764
764
#if EXTENSIVE_HEAP_PROFILING
765
- gc_dump_alloc_table ();
765
+ gc_dump_alloc_table (true, true );
766
766
#endif
767
767
768
768
return ptr_in ;
@@ -805,27 +805,59 @@ void gc_dump_info(void) {
805
805
(uint )info .num_1block , (uint )info .num_2block , (uint )info .max_block , (uint )info .max_free );
806
806
}
807
807
808
- void gc_dump_alloc_table (void ) {
808
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
809
+ // Power-of-two size classes. i.e. class[0] is how many single-block
810
+ // allocations are possible. class[5] is how many 32-block allocations.
811
+ #define NUM_FRAGMENTATION_CLASSES (20)
812
+
813
+ // Given a number of contiguous blocks, figure out how many allocations of
814
+ // each size class would fit in that many blocks.
815
+ STATIC void gc_update_fragmentation_stats (size_t n , size_t * frag_classes ) {
816
+ for (size_t c = 1 , i = 0 ; c < n && i < NUM_FRAGMENTATION_CLASSES ; c <<= 1 , ++ i ) {
817
+ frag_classes [i ] += n / c ;
818
+ }
819
+ }
820
+ #endif
821
+
822
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS || MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
823
+ void gc_dump_alloc_table (bool print_table , bool print_fragmentation ) {
809
824
GC_ENTER ();
825
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
810
826
static const size_t DUMP_BYTES_PER_LINE = 64 ;
827
+ #endif
811
828
#if !EXTENSIVE_HEAP_PROFILING
812
829
// When comparing heap output we don't want to print the starting
813
830
// pointer of the heap because it changes from run to run.
814
- mp_printf (& mp_plat_print , "GC memory layout; from %p:" , MP_STATE_MEM (gc_pool_start ));
831
+ if (print_table ) {
832
+ mp_printf (& mp_plat_print , "GC memory layout; from %p:" , MP_STATE_MEM (gc_pool_start ));
833
+ }
834
+ #endif
835
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
836
+ // How many consecutive free blocks.
837
+ size_t nfree = 0 ;
838
+ // Number of allocs that would succeed for 1, 2, 4, .. 2^n blocks.
839
+ size_t frag_classes [NUM_FRAGMENTATION_CLASSES ] = {0 };
815
840
#endif
816
841
for (size_t bl = 0 ; bl < MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; bl ++ ) {
817
- if (bl % DUMP_BYTES_PER_LINE == 0 ) {
842
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
843
+ if (print_table && bl % DUMP_BYTES_PER_LINE == 0 ) {
818
844
// a new line of blocks
819
845
{
820
846
// check if this line contains only free blocks
821
847
size_t bl2 = bl ;
822
848
while (bl2 < MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB && ATB_GET_KIND (bl2 ) == AT_FREE ) {
823
849
bl2 ++ ;
824
850
}
825
- if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE ) {
851
+ size_t skip = bl2 - bl ;
852
+ size_t lines = skip / DUMP_BYTES_PER_LINE ;
853
+ if (lines >= 2 ) {
826
854
// there are at least 2 lines containing only free blocks, so abbreviate their printing
827
- mp_printf (& mp_plat_print , "\n (%u lines all free)" , (uint )(bl2 - bl ) / DUMP_BYTES_PER_LINE );
828
- bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1 ));
855
+ mp_printf (& mp_plat_print , "\n (%u lines all free)" , (uint )lines );
856
+ skip = lines * DUMP_BYTES_PER_LINE ;
857
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
858
+ nfree += skip ;
859
+ #endif
860
+ bl += skip ;
829
861
if (bl >= MP_STATE_MEM (gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ) {
830
862
// got to end of heap
831
863
break ;
@@ -837,13 +869,22 @@ void gc_dump_alloc_table(void) {
837
869
//mp_printf(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff));
838
870
mp_printf (& mp_plat_print , "\n%05x: " , (uint )((bl * BYTES_PER_BLOCK ) & (uint32_t )0xfffff ));
839
871
}
872
+ #endif
873
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
840
874
int c = ' ' ;
875
+ #endif
841
876
switch (ATB_GET_KIND (bl )) {
842
877
case AT_FREE :
878
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
843
879
c = '.' ;
880
+ #endif
881
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
882
+ ++ nfree ;
883
+ #endif
844
884
break ;
845
885
/* this prints out if the object is reachable from BSS or STACK (for unix only)
846
886
case AT_HEAD: {
887
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
847
888
c = 'h';
848
889
void **ptrs = (void**)(void*)&mp_state_ctx;
849
890
mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t);
@@ -865,11 +906,13 @@ void gc_dump_alloc_table(void) {
865
906
}
866
907
}
867
908
}
868
- break;
909
+ #endif
910
+ goto reset_frag;
869
911
}
870
912
*/
871
913
/* this prints the uPy object type of the head block */
872
914
case AT_HEAD : {
915
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
873
916
void * * ptr = (void * * )(MP_STATE_MEM (gc_pool_start ) + bl * BYTES_PER_BLOCK );
874
917
if (* ptr == & mp_type_tuple ) {
875
918
c = 'T' ;
@@ -919,20 +962,47 @@ void gc_dump_alloc_table(void) {
919
962
}
920
963
#endif
921
964
}
922
- break ;
965
+ #endif
966
+ goto reset_frag ;
923
967
}
924
968
case AT_TAIL :
969
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
925
970
c = '=' ;
926
- break ;
971
+ #endif
972
+ goto reset_frag ;
927
973
case AT_MARK :
974
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
928
975
c = 'm' ;
976
+ #endif
977
+ goto reset_frag ;
978
+ reset_frag :
979
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
980
+ gc_update_fragmentation_stats (nfree , frag_classes );
981
+ nfree = 0 ;
982
+ #endif
929
983
break ;
930
984
}
931
- mp_printf (& mp_plat_print , "%c" , c );
985
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_BLOCKS
986
+ if (print_table ) {
987
+ mp_printf (& mp_plat_print , "%c" , c );
988
+ }
989
+ #endif
932
990
}
991
+ #if MICROPY_PY_MICROPYTHON_MEM_INFO_FRAGMENTATION
992
+ if (print_fragmentation ) {
993
+ gc_update_fragmentation_stats (nfree , frag_classes );
994
+ mp_print_str (& mp_plat_print , "Frag:" );
995
+ size_t c = 1 ;
996
+ for (int i = 0 ; i < NUM_FRAGMENTATION_CLASSES ; ++ i ) {
997
+ mp_printf (& mp_plat_print , " %u: %u," , c , frag_classes [i ]);
998
+ c <<= 1 ;
999
+ }
1000
+ }
1001
+ #endif
933
1002
mp_print_str (& mp_plat_print , "\n" );
934
1003
GC_EXIT ();
935
1004
}
1005
+ #endif
936
1006
937
1007
#if 0
938
1008
// For testing the GC functions
0 commit comments