diff options
Diffstat (limited to 'src/gc/gcrecord.h')
-rw-r--r-- | src/gc/gcrecord.h | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/src/gc/gcrecord.h b/src/gc/gcrecord.h new file mode 100644 index 0000000000..8c95ad04d3 --- /dev/null +++ b/src/gc/gcrecord.h @@ -0,0 +1,425 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + +Module Name: + + gcrecord.h + +--*/ + +#ifndef __gc_record_h__ +#define __gc_record_h__ + +#define max_generation 2 + +// We pack the dynamic tuning for deciding which gen to condemn in a uint32_t. +// We assume that 2 bits are enough to represent the generation. +#define bits_generation 2 +#define generation_mask (~(~0u << bits_generation)) +//=======================note !!!===================================// +// If you add stuff to this enum, remember to update total_gen_reasons +// and record_condemn_gen_reasons below. +//=======================note !!!===================================// + +// These are condemned reasons related to generations. +// Each reason takes up 2 bits as we have 3 generations. +// So we can store up to 16 reasons in this uint32_t. +// They need processing before being used. +// See the set and the get method for details. +enum gc_condemn_reason_gen +{ + gen_initial = 0, // indicates the initial gen to condemn. + gen_final_per_heap = 1, // indicates the final gen to condemn per heap. + gen_alloc_budget = 2, // indicates which gen's budget is exceeded. + gen_time_tuning = 3, // indicates the gen number that time based tuning decided. + gcrg_max = 4 +}; + +// These are condemned reasons related to conditions we are in. +// For example, we are in very high memory load which is a condition. +// Each condition takes up a single bit indicates TRUE or FALSE. +// We can store 32 of these. +enum gc_condemn_reason_condition +{ + gen_induced_fullgc_p = 0, + gen_expand_fullgc_p = 1, + gen_high_mem_p = 2, + gen_very_high_mem_p = 3, + gen_low_ephemeral_p = 4, + gen_low_card_p = 5, + gen_eph_high_frag_p = 6, + gen_max_high_frag_p = 7, + gen_max_high_frag_e_p = 8, + gen_max_high_frag_m_p = 9, + gen_max_high_frag_vm_p = 10, + gen_max_gen1 = 11, + gen_before_oom = 12, + gen_gen2_too_small = 13, + gen_induced_noforce_p = 14, + gen_before_bgc = 15, + gen_almost_max_alloc = 16, + gcrc_max = 17 +}; + +#ifdef DT_LOG +static char* record_condemn_reasons_gen_header = "[cg]i|f|a|t|"; +static char* record_condemn_reasons_condition_header = "[cc]i|e|h|v|l|l|e|m|m|m|m|g|o|s|n|b|a|"; +static char char_gen_number[4] = {'0', '1', '2', '3'}; +#endif //DT_LOG + +class gen_to_condemn_tuning +{ + uint32_t condemn_reasons_gen; + uint32_t condemn_reasons_condition; + +#ifdef DT_LOG + char str_reasons_gen[64]; + char str_reasons_condition[64]; +#endif //DT_LOG + + void init_str() + { +#ifdef DT_LOG + memset (str_reasons_gen, '|', sizeof (char) * 64); + str_reasons_gen[gcrg_max*2] = 0; + memset (str_reasons_condition, '|', sizeof (char) * 64); + str_reasons_condition[gcrc_max*2] = 0; +#endif //DT_LOG + } + +public: + void init() + { + condemn_reasons_gen = 0; + condemn_reasons_condition = 0; + init_str(); + } + + void init (gen_to_condemn_tuning* reasons) + { + condemn_reasons_gen = reasons->condemn_reasons_gen; + condemn_reasons_condition = reasons->condemn_reasons_condition; + init_str(); + } + + void set_gen (gc_condemn_reason_gen condemn_gen_reason, uint32_t value) + { + assert ((value & (~generation_mask)) == 0); + condemn_reasons_gen |= (value << (condemn_gen_reason * 2)); + } + + void set_condition (gc_condemn_reason_condition condemn_gen_reason) + { + condemn_reasons_condition |= (1 << condemn_gen_reason); + } + + // This checks if condition_to_check is the only condition set. + BOOL is_only_condition (gc_condemn_reason_condition condition_to_check) + { + uint32_t temp_conditions = 1 << condition_to_check; + return !(condemn_reasons_condition ^ temp_conditions); + } + + uint32_t get_gen (gc_condemn_reason_gen condemn_gen_reason) + { + uint32_t value = ((condemn_reasons_gen >> (condemn_gen_reason * 2)) & generation_mask); + return value; + } + + uint32_t get_condition (gc_condemn_reason_condition condemn_gen_reason) + { + uint32_t value = (condemn_reasons_condition & (1 << condemn_gen_reason)); + return value; + } + + uint32_t get_reasons0() + { + return condemn_reasons_gen; + } + + uint32_t get_reasons1() + { + return condemn_reasons_condition; + } + +#ifdef DT_LOG + char get_gen_char (uint32_t value) + { + return char_gen_number[value]; + } + char get_condition_char (uint32_t value) + { + return (value ? 'Y' : 'N'); + } +#endif //DT_LOG + + void print (int heap_num); +}; + +// Right now these are all size_t's but if you add a type that requires +// padding you should add a pragma pack here since I am firing this as +// a struct in an ETW event. +struct gc_generation_data +{ + // data recorded at the beginning of a GC + size_t size_before; // including fragmentation. + size_t free_list_space_before; + size_t free_obj_space_before; + + // data recorded at the end of a GC + size_t size_after; // including fragmentation. + size_t free_list_space_after; + size_t free_obj_space_after; + size_t in; + size_t pinned_surv; + size_t npinned_surv; + size_t new_allocation; + + void print (int heap_num, int gen_num); +}; + +struct maxgen_size_increase +{ + size_t free_list_allocated; + size_t free_list_rejected; + size_t end_seg_allocated; + size_t condemned_allocated; + size_t pinned_allocated; + size_t pinned_allocated_advance; + uint32_t running_free_list_efficiency; +}; + +// The following indicates various mechanisms and one value +// related to each one. Each value has its corresponding string +// representation so if you change the enum's, make sure you +// also add its string form. + +// Note that if we are doing a gen1 GC, we won't +// really expand the heap if we are reusing, but +// we'll record the can_expand_into_p result here. +enum gc_heap_expand_mechanism +{ + expand_reuse_normal = 0, + expand_reuse_bestfit = 1, + expand_new_seg_ep = 2, // new seg with ephemeral promotion + expand_new_seg = 3, + expand_no_memory = 4, // we can't get a new seg. + expand_next_full_gc = 5, + max_expand_mechanisms_count = 6 +}; + +#ifdef DT_LOG +static char* str_heap_expand_mechanisms[] = +{ + "reused seg with normal fit", + "reused seg with best fit", + "expand promoting eph", + "expand with a new seg", + "no memory for a new seg", + "expand in next full GC" +}; +#endif //DT_LOG + +enum gc_heap_compact_reason +{ + compact_low_ephemeral = 0, + compact_high_frag = 1, + compact_no_gaps = 2, + compact_loh_forced = 3, + compact_last_gc = 4, + compact_induced_compacting = 5, + compact_fragmented_gen0 = 6, + compact_high_mem_load = 7, + compact_high_mem_frag = 8, + compact_vhigh_mem_frag = 9, + compact_no_gc_mode = 10, + max_compact_reasons_count = 11 +}; + +#ifndef DACCESS_COMPILE +static BOOL gc_heap_compact_reason_mandatory_p[] = +{ + TRUE, //compact_low_ephemeral = 0, + FALSE, //compact_high_frag = 1, + TRUE, //compact_no_gaps = 2, + TRUE, //compact_loh_forced = 3, + TRUE, //compact_last_gc = 4 + TRUE, //compact_induced_compacting = 5, + FALSE, //compact_fragmented_gen0 = 6, + FALSE, //compact_high_mem_load = 7, + TRUE, //compact_high_mem_frag = 8, + TRUE, //compact_vhigh_mem_frag = 9, + TRUE //compact_no_gc_mode = 10 +}; + +static BOOL gc_expand_mechanism_mandatory_p[] = +{ + FALSE, //expand_reuse_normal = 0, + TRUE, //expand_reuse_bestfit = 1, + FALSE, //expand_new_seg_ep = 2, // new seg with ephemeral promotion + TRUE, //expand_new_seg = 3, + FALSE, //expand_no_memory = 4, // we can't get a new seg. + TRUE //expand_next_full_gc = 5 +}; +#endif //!DACCESS_COMPILE + +#ifdef DT_LOG +static char* str_heap_compact_reasons[] = +{ + "low on ephemeral space", + "high fragmetation", + "couldn't allocate gaps", + "user specfied compact LOH", + "last GC before OOM", + "induced compacting GC", + "fragmented gen0 (ephemeral GC)", + "high memory load (ephemeral GC)", + "high memory load and frag", + "very high memory load and frag", + "no gc mode" +}; +#endif //DT_LOG + +enum gc_mechanism_per_heap +{ + gc_heap_expand, + gc_heap_compact, + max_mechanism_per_heap +}; + +enum gc_mechanism_bit_per_heap +{ + gc_mark_list_bit = 0, + gc_demotion_bit = 1, + max_gc_mechanism_bits_count = 2 +}; + +#ifdef DT_LOG +struct gc_mechanism_descr +{ + char* name; + char** descr; +}; + +static gc_mechanism_descr gc_mechanisms_descr[max_mechanism_per_heap] = +{ + {"expanded heap ", str_heap_expand_mechanisms}, + {"compacted because of ", str_heap_compact_reasons} +}; +#endif //DT_LOG + +int index_of_set_bit (size_t power2); + +#define mechanism_mask (1 << (sizeof (uint32_t) * 8 - 1)) +// interesting per heap data we want to record for each GC. +class gc_history_per_heap +{ +public: + gc_generation_data gen_data[max_generation+2]; + maxgen_size_increase maxgen_size_info; + gen_to_condemn_tuning gen_to_condemn_reasons; + + // The mechanisms data is compacted in the following way: + // most significant bit indicates if we did the operation. + // the rest of the bits indicate the reason/mechanism + // why we chose to do the operation. For example: + // if we did a heap expansion using best fit we'd have + // 0x80000002 for the gc_heap_expand mechanism. + // Only one value is possible for each mechanism - meaning the + // values are all exclusive + // TODO: for the config stuff I need to think more about how to represent this + // because we might want to know all reasons (at least all mandatory ones) for + // compact. + // TODO: no need to the MSB for this + uint32_t mechanisms[max_mechanism_per_heap]; + + // Each bit in this uint32_t represent if a mechanism was used or not. + uint32_t machanism_bits; + + uint32_t heap_index; + + size_t extra_gen0_committed; + + void set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value); + + void set_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) + { + machanism_bits |= 1 << mech_bit; + } + + void clear_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) + { + machanism_bits &= ~(1 << mech_bit); + } + + BOOL is_mechanism_bit_set (gc_mechanism_bit_per_heap mech_bit) + { + return (machanism_bits & (1 << mech_bit)); + } + + void clear_mechanism(gc_mechanism_per_heap mechanism_per_heap) + { + uint32_t* mechanism = &mechanisms[mechanism_per_heap]; + *mechanism = 0; + } + + int get_mechanism (gc_mechanism_per_heap mechanism_per_heap) + { + uint32_t mechanism = mechanisms[mechanism_per_heap]; + + if (mechanism & mechanism_mask) + { + int index = index_of_set_bit ((size_t)(mechanism & (~mechanism_mask))); + assert (index != -1); + return index; + } + + return -1; + } + + void print(); +}; + +// we store up to 32 boolean settings. +enum gc_global_mechanism_p +{ + global_concurrent = 0, + global_compaction = 1, + global_promotion = 2, + global_demotion = 3, + global_card_bundles = 4, + global_elevation = 5, + max_global_mechanisms_count +}; + +struct gc_history_global +{ + // We may apply other factors after we calculated gen0 budget in + // desired_new_allocation such as equalization or smoothing so + // record the final budget here. + size_t final_youngest_desired; + uint32_t num_heaps; + int condemned_generation; + int gen0_reduction_count; + gc_reason reason; + int pause_mode; + uint32_t mem_pressure; + uint32_t global_mechanims_p; + + void set_mechanism_p (gc_global_mechanism_p mechanism) + { + global_mechanims_p |= (1 << mechanism); + } + + BOOL get_mechanism_p (gc_global_mechanism_p mechanism) + { + return (global_mechanims_p & (1 << mechanism)); + } + + void print(); +}; + +#endif //__gc_record_h__ |