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