1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 /*++ 6 7 Module Name: 8 9 gcrecord.h 10 11 --*/ 12 13 #ifndef __gc_record_h__ 14 #define __gc_record_h__ 15 16 //#define max_generation 2 17 18 // We pack the dynamic tuning for deciding which gen to condemn in a uint32_t. 19 // We assume that 2 bits are enough to represent the generation. 20 #define bits_generation 2 21 #define generation_mask (~(~0u << bits_generation)) 22 //=======================note !!!===================================// 23 // If you add stuff to this enum, remember to update total_gen_reasons 24 // and record_condemn_gen_reasons below. 25 //=======================note !!!===================================// 26 27 // These are condemned reasons related to generations. 28 // Each reason takes up 2 bits as we have 3 generations. 29 // So we can store up to 16 reasons in this uint32_t. 30 // They need processing before being used. 31 // See the set and the get method for details. 32 enum gc_condemn_reason_gen 33 { 34 gen_initial = 0, // indicates the initial gen to condemn. 35 gen_final_per_heap = 1, // indicates the final gen to condemn per heap. 36 gen_alloc_budget = 2, // indicates which gen's budget is exceeded. 37 gen_time_tuning = 3, // indicates the gen number that time based tuning decided. 38 gcrg_max = 4 39 }; 40 41 // These are condemned reasons related to conditions we are in. 42 // For example, we are in very high memory load which is a condition. 43 // Each condition takes up a single bit indicates TRUE or FALSE. 44 // We can store 32 of these. 45 enum gc_condemn_reason_condition 46 { 47 gen_induced_fullgc_p = 0, 48 gen_expand_fullgc_p = 1, 49 gen_high_mem_p = 2, 50 gen_very_high_mem_p = 3, 51 gen_low_ephemeral_p = 4, 52 gen_low_card_p = 5, 53 gen_eph_high_frag_p = 6, 54 gen_max_high_frag_p = 7, 55 gen_max_high_frag_e_p = 8, 56 gen_max_high_frag_m_p = 9, 57 gen_max_high_frag_vm_p = 10, 58 gen_max_gen1 = 11, 59 gen_before_oom = 12, 60 gen_gen2_too_small = 13, 61 gen_induced_noforce_p = 14, 62 gen_before_bgc = 15, 63 gen_almost_max_alloc = 16, 64 gcrc_max = 17 65 }; 66 67 #ifdef DT_LOG 68 static char* record_condemn_reasons_gen_header = "[cg]i|f|a|t|"; 69 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|"; 70 static char char_gen_number[4] = {'0', '1', '2', '3'}; 71 #endif //DT_LOG 72 73 class gen_to_condemn_tuning 74 { 75 uint32_t condemn_reasons_gen; 76 uint32_t condemn_reasons_condition; 77 78 #ifdef DT_LOG 79 char str_reasons_gen[64]; 80 char str_reasons_condition[64]; 81 #endif //DT_LOG 82 init_str()83 void init_str() 84 { 85 #ifdef DT_LOG 86 memset (str_reasons_gen, '|', sizeof (char) * 64); 87 str_reasons_gen[gcrg_max*2] = 0; 88 memset (str_reasons_condition, '|', sizeof (char) * 64); 89 str_reasons_condition[gcrc_max*2] = 0; 90 #endif //DT_LOG 91 } 92 93 public: init()94 void init() 95 { 96 condemn_reasons_gen = 0; 97 condemn_reasons_condition = 0; 98 init_str(); 99 } 100 init(gen_to_condemn_tuning * reasons)101 void init (gen_to_condemn_tuning* reasons) 102 { 103 condemn_reasons_gen = reasons->condemn_reasons_gen; 104 condemn_reasons_condition = reasons->condemn_reasons_condition; 105 init_str(); 106 } 107 set_gen(gc_condemn_reason_gen condemn_gen_reason,uint32_t value)108 void set_gen (gc_condemn_reason_gen condemn_gen_reason, uint32_t value) 109 { 110 assert ((value & (~generation_mask)) == 0); 111 condemn_reasons_gen |= (value << (condemn_gen_reason * 2)); 112 } 113 set_condition(gc_condemn_reason_condition condemn_gen_reason)114 void set_condition (gc_condemn_reason_condition condemn_gen_reason) 115 { 116 condemn_reasons_condition |= (1 << condemn_gen_reason); 117 } 118 119 // This checks if condition_to_check is the only condition set. is_only_condition(gc_condemn_reason_condition condition_to_check)120 BOOL is_only_condition (gc_condemn_reason_condition condition_to_check) 121 { 122 uint32_t temp_conditions = 1 << condition_to_check; 123 return !(condemn_reasons_condition ^ temp_conditions); 124 } 125 get_gen(gc_condemn_reason_gen condemn_gen_reason)126 uint32_t get_gen (gc_condemn_reason_gen condemn_gen_reason) 127 { 128 uint32_t value = ((condemn_reasons_gen >> (condemn_gen_reason * 2)) & generation_mask); 129 return value; 130 } 131 get_condition(gc_condemn_reason_condition condemn_gen_reason)132 uint32_t get_condition (gc_condemn_reason_condition condemn_gen_reason) 133 { 134 uint32_t value = (condemn_reasons_condition & (1 << condemn_gen_reason)); 135 return value; 136 } 137 get_reasons0()138 uint32_t get_reasons0() 139 { 140 return condemn_reasons_gen; 141 } 142 get_reasons1()143 uint32_t get_reasons1() 144 { 145 return condemn_reasons_condition; 146 } 147 148 #ifdef DT_LOG get_gen_char(uint32_t value)149 char get_gen_char (uint32_t value) 150 { 151 return char_gen_number[value]; 152 } get_condition_char(uint32_t value)153 char get_condition_char (uint32_t value) 154 { 155 return (value ? 'Y' : 'N'); 156 } 157 #endif //DT_LOG 158 159 void print (int heap_num); 160 }; 161 162 // Right now these are all size_t's but if you add a type that requires 163 // padding you should add a pragma pack here since I am firing this as 164 // a struct in an ETW event. 165 struct gc_generation_data 166 { 167 // data recorded at the beginning of a GC 168 size_t size_before; // including fragmentation. 169 size_t free_list_space_before; 170 size_t free_obj_space_before; 171 172 // data recorded at the end of a GC 173 size_t size_after; // including fragmentation. 174 size_t free_list_space_after; 175 size_t free_obj_space_after; 176 size_t in; 177 size_t pinned_surv; 178 size_t npinned_surv; 179 size_t new_allocation; 180 181 void print (int heap_num, int gen_num); 182 }; 183 184 struct maxgen_size_increase 185 { 186 size_t free_list_allocated; 187 size_t free_list_rejected; 188 size_t end_seg_allocated; 189 size_t condemned_allocated; 190 size_t pinned_allocated; 191 size_t pinned_allocated_advance; 192 uint32_t running_free_list_efficiency; 193 }; 194 195 // The following indicates various mechanisms and one value 196 // related to each one. Each value has its corresponding string 197 // representation so if you change the enum's, make sure you 198 // also add its string form. 199 200 // Note that if we are doing a gen1 GC, we won't 201 // really expand the heap if we are reusing, but 202 // we'll record the can_expand_into_p result here. 203 enum gc_heap_expand_mechanism 204 { 205 expand_reuse_normal = 0, 206 expand_reuse_bestfit = 1, 207 expand_new_seg_ep = 2, // new seg with ephemeral promotion 208 expand_new_seg = 3, 209 expand_no_memory = 4, // we can't get a new seg. 210 expand_next_full_gc = 5, 211 max_expand_mechanisms_count = 6 212 }; 213 214 #ifdef DT_LOG 215 static char* str_heap_expand_mechanisms[] = 216 { 217 "reused seg with normal fit", 218 "reused seg with best fit", 219 "expand promoting eph", 220 "expand with a new seg", 221 "no memory for a new seg", 222 "expand in next full GC" 223 }; 224 #endif //DT_LOG 225 226 enum gc_heap_compact_reason 227 { 228 compact_low_ephemeral = 0, 229 compact_high_frag = 1, 230 compact_no_gaps = 2, 231 compact_loh_forced = 3, 232 compact_last_gc = 4, 233 compact_induced_compacting = 5, 234 compact_fragmented_gen0 = 6, 235 compact_high_mem_load = 7, 236 compact_high_mem_frag = 8, 237 compact_vhigh_mem_frag = 9, 238 compact_no_gc_mode = 10, 239 max_compact_reasons_count = 11 240 }; 241 242 #ifndef DACCESS_COMPILE 243 static BOOL gc_heap_compact_reason_mandatory_p[] = 244 { 245 TRUE, //compact_low_ephemeral = 0, 246 FALSE, //compact_high_frag = 1, 247 TRUE, //compact_no_gaps = 2, 248 TRUE, //compact_loh_forced = 3, 249 TRUE, //compact_last_gc = 4 250 TRUE, //compact_induced_compacting = 5, 251 FALSE, //compact_fragmented_gen0 = 6, 252 FALSE, //compact_high_mem_load = 7, 253 TRUE, //compact_high_mem_frag = 8, 254 TRUE, //compact_vhigh_mem_frag = 9, 255 TRUE //compact_no_gc_mode = 10 256 }; 257 258 static BOOL gc_expand_mechanism_mandatory_p[] = 259 { 260 FALSE, //expand_reuse_normal = 0, 261 TRUE, //expand_reuse_bestfit = 1, 262 FALSE, //expand_new_seg_ep = 2, // new seg with ephemeral promotion 263 TRUE, //expand_new_seg = 3, 264 FALSE, //expand_no_memory = 4, // we can't get a new seg. 265 TRUE //expand_next_full_gc = 5 266 }; 267 #endif //!DACCESS_COMPILE 268 269 #ifdef DT_LOG 270 static char* str_heap_compact_reasons[] = 271 { 272 "low on ephemeral space", 273 "high fragmetation", 274 "couldn't allocate gaps", 275 "user specfied compact LOH", 276 "last GC before OOM", 277 "induced compacting GC", 278 "fragmented gen0 (ephemeral GC)", 279 "high memory load (ephemeral GC)", 280 "high memory load and frag", 281 "very high memory load and frag", 282 "no gc mode" 283 }; 284 #endif //DT_LOG 285 286 enum gc_mechanism_per_heap 287 { 288 gc_heap_expand, 289 gc_heap_compact, 290 max_mechanism_per_heap 291 }; 292 293 enum gc_mechanism_bit_per_heap 294 { 295 gc_mark_list_bit = 0, 296 gc_demotion_bit = 1, 297 max_gc_mechanism_bits_count = 2 298 }; 299 300 #ifdef DT_LOG 301 struct gc_mechanism_descr 302 { 303 char* name; 304 char** descr; 305 }; 306 307 static gc_mechanism_descr gc_mechanisms_descr[max_mechanism_per_heap] = 308 { 309 {"expanded heap ", str_heap_expand_mechanisms}, 310 {"compacted because of ", str_heap_compact_reasons} 311 }; 312 #endif //DT_LOG 313 314 int index_of_set_bit (size_t power2); 315 316 #define mechanism_mask (1 << (sizeof (uint32_t) * 8 - 1)) 317 // interesting per heap data we want to record for each GC. 318 class gc_history_per_heap 319 { 320 public: 321 gc_generation_data gen_data[max_generation+2]; 322 maxgen_size_increase maxgen_size_info; 323 gen_to_condemn_tuning gen_to_condemn_reasons; 324 325 // The mechanisms data is compacted in the following way: 326 // most significant bit indicates if we did the operation. 327 // the rest of the bits indicate the reason/mechanism 328 // why we chose to do the operation. For example: 329 // if we did a heap expansion using best fit we'd have 330 // 0x80000002 for the gc_heap_expand mechanism. 331 // Only one value is possible for each mechanism - meaning the 332 // values are all exclusive 333 // TODO: for the config stuff I need to think more about how to represent this 334 // because we might want to know all reasons (at least all mandatory ones) for 335 // compact. 336 // TODO: no need to the MSB for this 337 uint32_t mechanisms[max_mechanism_per_heap]; 338 339 // Each bit in this uint32_t represent if a mechanism was used or not. 340 uint32_t machanism_bits; 341 342 uint32_t heap_index; 343 344 size_t extra_gen0_committed; 345 346 void set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value); 347 set_mechanism_bit(gc_mechanism_bit_per_heap mech_bit)348 void set_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) 349 { 350 machanism_bits |= 1 << mech_bit; 351 } 352 clear_mechanism_bit(gc_mechanism_bit_per_heap mech_bit)353 void clear_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) 354 { 355 machanism_bits &= ~(1 << mech_bit); 356 } 357 is_mechanism_bit_set(gc_mechanism_bit_per_heap mech_bit)358 BOOL is_mechanism_bit_set (gc_mechanism_bit_per_heap mech_bit) 359 { 360 return (machanism_bits & (1 << mech_bit)); 361 } 362 clear_mechanism(gc_mechanism_per_heap mechanism_per_heap)363 void clear_mechanism(gc_mechanism_per_heap mechanism_per_heap) 364 { 365 uint32_t* mechanism = &mechanisms[mechanism_per_heap]; 366 *mechanism = 0; 367 } 368 get_mechanism(gc_mechanism_per_heap mechanism_per_heap)369 int get_mechanism (gc_mechanism_per_heap mechanism_per_heap) 370 { 371 uint32_t mechanism = mechanisms[mechanism_per_heap]; 372 373 if (mechanism & mechanism_mask) 374 { 375 int index = index_of_set_bit ((size_t)(mechanism & (~mechanism_mask))); 376 assert (index != -1); 377 return index; 378 } 379 380 return -1; 381 } 382 383 void print(); 384 }; 385 386 // we store up to 32 boolean settings. 387 enum gc_global_mechanism_p 388 { 389 global_concurrent = 0, 390 global_compaction = 1, 391 global_promotion = 2, 392 global_demotion = 3, 393 global_card_bundles = 4, 394 global_elevation = 5, 395 max_global_mechanisms_count 396 }; 397 398 struct gc_history_global 399 { 400 // We may apply other factors after we calculated gen0 budget in 401 // desired_new_allocation such as equalization or smoothing so 402 // record the final budget here. 403 size_t final_youngest_desired; 404 uint32_t num_heaps; 405 int condemned_generation; 406 int gen0_reduction_count; 407 gc_reason reason; 408 int pause_mode; 409 uint32_t mem_pressure; 410 uint32_t global_mechanims_p; 411 set_mechanism_pgc_history_global412 void set_mechanism_p (gc_global_mechanism_p mechanism) 413 { 414 global_mechanims_p |= (1 << mechanism); 415 } 416 get_mechanism_pgc_history_global417 BOOL get_mechanism_p (gc_global_mechanism_p mechanism) 418 { 419 return (global_mechanims_p & (1 << mechanism)); 420 } 421 422 void print(); 423 }; 424 425 #endif //__gc_record_h__ 426