| 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 | |