| 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 |  | 
|---|
| 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: | 
|---|
| 94 | void init() | 
|---|
| 95 | { | 
|---|
| 96 | condemn_reasons_gen = 0; | 
|---|
| 97 | condemn_reasons_condition = 0; | 
|---|
| 98 | init_str(); | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 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 |  | 
|---|
| 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 |  | 
|---|
| 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. | 
|---|
| 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 |  | 
|---|
| 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 |  | 
|---|
| 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 |  | 
|---|
| 138 | uint32_t get_reasons0() | 
|---|
| 139 | { | 
|---|
| 140 | return condemn_reasons_gen; | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | uint32_t get_reasons1() | 
|---|
| 144 | { | 
|---|
| 145 | return condemn_reasons_condition; | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | #ifdef DT_LOG | 
|---|
| 149 | char get_gen_char (uint32_t value) | 
|---|
| 150 | { | 
|---|
| 151 | return char_gen_number[value]; | 
|---|
| 152 | } | 
|---|
| 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 fragmentation", | 
|---|
| 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 | // Get the 0-based index of the most-significant bit in the value. | 
|---|
| 315 | // Returns -1 if the input value is zero (i.e. has no set bits). | 
|---|
| 316 | int index_of_highest_set_bit (size_t value); | 
|---|
| 317 |  | 
|---|
| 318 | #define mechanism_mask (1 << (sizeof (uint32_t) * 8 - 1)) | 
|---|
| 319 | // interesting per heap data we want to record for each GC. | 
|---|
| 320 | class gc_history_per_heap | 
|---|
| 321 | { | 
|---|
| 322 | public: | 
|---|
| 323 | gc_generation_data gen_data[max_generation+2]; | 
|---|
| 324 | maxgen_size_increase maxgen_size_info; | 
|---|
| 325 | gen_to_condemn_tuning gen_to_condemn_reasons; | 
|---|
| 326 |  | 
|---|
| 327 | // The mechanisms data is compacted in the following way: | 
|---|
| 328 | // most significant bit indicates if we did the operation. | 
|---|
| 329 | // the rest of the bits indicate the reason/mechanism | 
|---|
| 330 | // why we chose to do the operation. For example: | 
|---|
| 331 | // if we did a heap expansion using best fit we'd have | 
|---|
| 332 | // 0x80000002 for the gc_heap_expand mechanism. | 
|---|
| 333 | // Only one value is possible for each mechanism - meaning the | 
|---|
| 334 | // values are all exclusive | 
|---|
| 335 | // TODO: for the config stuff I need to think more about how to represent this | 
|---|
| 336 | // because we might want to know all reasons (at least all mandatory ones) for | 
|---|
| 337 | // compact. | 
|---|
| 338 | // TODO: no need to the MSB for this | 
|---|
| 339 | uint32_t mechanisms[max_mechanism_per_heap]; | 
|---|
| 340 |  | 
|---|
| 341 | // Each bit in this uint32_t represent if a mechanism was used or not. | 
|---|
| 342 | uint32_t machanism_bits; | 
|---|
| 343 |  | 
|---|
| 344 | uint32_t heap_index; | 
|---|
| 345 |  | 
|---|
| 346 | size_t ; | 
|---|
| 347 |  | 
|---|
| 348 | void set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value); | 
|---|
| 349 |  | 
|---|
| 350 | void set_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) | 
|---|
| 351 | { | 
|---|
| 352 | machanism_bits |= 1 << mech_bit; | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | void clear_mechanism_bit (gc_mechanism_bit_per_heap mech_bit) | 
|---|
| 356 | { | 
|---|
| 357 | machanism_bits &= ~(1 << mech_bit); | 
|---|
| 358 | } | 
|---|
| 359 |  | 
|---|
| 360 | BOOL is_mechanism_bit_set (gc_mechanism_bit_per_heap mech_bit) | 
|---|
| 361 | { | 
|---|
| 362 | return (machanism_bits & (1 << mech_bit)); | 
|---|
| 363 | } | 
|---|
| 364 |  | 
|---|
| 365 | void clear_mechanism(gc_mechanism_per_heap mechanism_per_heap) | 
|---|
| 366 | { | 
|---|
| 367 | uint32_t* mechanism = &mechanisms[mechanism_per_heap]; | 
|---|
| 368 | *mechanism = 0; | 
|---|
| 369 | } | 
|---|
| 370 |  | 
|---|
| 371 | int get_mechanism (gc_mechanism_per_heap mechanism_per_heap) | 
|---|
| 372 | { | 
|---|
| 373 | uint32_t mechanism = mechanisms[mechanism_per_heap]; | 
|---|
| 374 |  | 
|---|
| 375 | if (mechanism & mechanism_mask) | 
|---|
| 376 | { | 
|---|
| 377 | int index = index_of_highest_set_bit ((size_t)(mechanism & (~mechanism_mask))); | 
|---|
| 378 | assert (index != -1); | 
|---|
| 379 | return index; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | return -1; | 
|---|
| 383 | } | 
|---|
| 384 |  | 
|---|
| 385 | void print(); | 
|---|
| 386 | }; | 
|---|
| 387 |  | 
|---|
| 388 | // we store up to 32 boolean settings. | 
|---|
| 389 | enum gc_global_mechanism_p | 
|---|
| 390 | { | 
|---|
| 391 | global_concurrent = 0, | 
|---|
| 392 | global_compaction = 1, | 
|---|
| 393 | global_promotion = 2, | 
|---|
| 394 | global_demotion = 3, | 
|---|
| 395 | global_card_bundles = 4, | 
|---|
| 396 | global_elevation = 5, | 
|---|
| 397 | max_global_mechanisms_count | 
|---|
| 398 | }; | 
|---|
| 399 |  | 
|---|
| 400 | struct gc_history_global | 
|---|
| 401 | { | 
|---|
| 402 | // We may apply other factors after we calculated gen0 budget in | 
|---|
| 403 | // desired_new_allocation such as equalization or smoothing so | 
|---|
| 404 | // record the final budget here. | 
|---|
| 405 | size_t final_youngest_desired; | 
|---|
| 406 | uint32_t num_heaps; | 
|---|
| 407 | int condemned_generation; | 
|---|
| 408 | int gen0_reduction_count; | 
|---|
| 409 | gc_reason reason; | 
|---|
| 410 | int pause_mode; | 
|---|
| 411 | uint32_t mem_pressure; | 
|---|
| 412 | uint32_t global_mechanims_p; | 
|---|
| 413 |  | 
|---|
| 414 | void set_mechanism_p (gc_global_mechanism_p mechanism) | 
|---|
| 415 | { | 
|---|
| 416 | global_mechanims_p |= (1 << mechanism); | 
|---|
| 417 | } | 
|---|
| 418 |  | 
|---|
| 419 | BOOL get_mechanism_p (gc_global_mechanism_p mechanism) | 
|---|
| 420 | { | 
|---|
| 421 | return (global_mechanims_p & (1 << mechanism)); | 
|---|
| 422 | } | 
|---|
| 423 |  | 
|---|
| 424 | void print(); | 
|---|
| 425 | }; | 
|---|
| 426 |  | 
|---|
| 427 | #endif //__gc_record_h__ | 
|---|
| 428 |  | 
|---|