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