summaryrefslogtreecommitdiff
path: root/src/gc/gcrecord.h
blob: c00f8b0826f13a6292605fdf3daa88006864c521 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*++

Module Name:

    gcrecord.h

--*/

#ifndef __gc_record_h__
#define __gc_record_h__

//#define max_generation 2

// We pack the dynamic tuning for deciding which gen to condemn in a uint32_t.
// We assume that 2 bits are enough to represent the generation. 
#define bits_generation 2
#define generation_mask (~(~0u << bits_generation))
//=======================note !!!===================================//
// If you add stuff to this enum, remember to update total_gen_reasons
// and record_condemn_gen_reasons below.
//=======================note !!!===================================//

// These are condemned reasons related to generations.
// Each reason takes up 2 bits as we have 3 generations.
// So we can store up to 16 reasons in this uint32_t.
// They need processing before being used.
// See the set and the get method for details.
enum gc_condemn_reason_gen
{
    gen_initial         = 0, // indicates the initial gen to condemn.
    gen_final_per_heap  = 1, // indicates the final gen to condemn per heap.
    gen_alloc_budget    = 2, // indicates which gen's budget is exceeded.
    gen_time_tuning     = 3, // indicates the gen number that time based tuning decided.
    gcrg_max = 4
};

// These are condemned reasons related to conditions we are in.
// For example, we are in very high memory load which is a condition. 
// Each condition takes up a single bit indicates TRUE or FALSE.
// We can store 32 of these.
enum gc_condemn_reason_condition
{
    gen_induced_fullgc_p  = 0,
    gen_expand_fullgc_p = 1,
    gen_high_mem_p = 2,
    gen_very_high_mem_p = 3,
    gen_low_ephemeral_p = 4,
    gen_low_card_p = 5,
    gen_eph_high_frag_p = 6,
    gen_max_high_frag_p = 7,
    gen_max_high_frag_e_p = 8,
    gen_max_high_frag_m_p = 9, 
    gen_max_high_frag_vm_p = 10,
    gen_max_gen1 = 11,
    gen_before_oom = 12,
    gen_gen2_too_small = 13,
    gen_induced_noforce_p = 14,
    gen_before_bgc = 15,
    gen_almost_max_alloc = 16,
    gcrc_max = 17
};

#ifdef DT_LOG
static char* record_condemn_reasons_gen_header = "[cg]i|f|a|t|";
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|";
static char char_gen_number[4] = {'0', '1', '2', '3'};
#endif //DT_LOG

class gen_to_condemn_tuning
{
    uint32_t condemn_reasons_gen;
    uint32_t condemn_reasons_condition;

#ifdef DT_LOG
    char str_reasons_gen[64];
    char str_reasons_condition[64];
#endif //DT_LOG

    void init_str()
    {
#ifdef DT_LOG
        memset (str_reasons_gen, '|', sizeof (char) * 64);
        str_reasons_gen[gcrg_max*2] = 0;
        memset (str_reasons_condition, '|', sizeof (char) * 64);
        str_reasons_condition[gcrc_max*2] = 0;
#endif //DT_LOG
    }

public:
    void init()
    {
        condemn_reasons_gen = 0;
        condemn_reasons_condition = 0;
        init_str();
    }

    void init (gen_to_condemn_tuning* reasons)
    {
        condemn_reasons_gen = reasons->condemn_reasons_gen;
        condemn_reasons_condition = reasons->condemn_reasons_condition;
        init_str();
    }

    void set_gen (gc_condemn_reason_gen condemn_gen_reason, uint32_t value)
    {
        assert ((value & (~generation_mask)) == 0);
        condemn_reasons_gen |= (value << (condemn_gen_reason * 2));
    }

    void set_condition (gc_condemn_reason_condition condemn_gen_reason)
    {
        condemn_reasons_condition |= (1 << condemn_gen_reason);
    }

    // This checks if condition_to_check is the only condition set.
    BOOL is_only_condition (gc_condemn_reason_condition condition_to_check)
    {
        uint32_t temp_conditions = 1 << condition_to_check;
        return !(condemn_reasons_condition ^ temp_conditions);
    }

    uint32_t get_gen (gc_condemn_reason_gen condemn_gen_reason)
    {
        uint32_t value = ((condemn_reasons_gen >> (condemn_gen_reason * 2)) & generation_mask);
        return value;
    }

    uint32_t get_condition (gc_condemn_reason_condition condemn_gen_reason)
    {
        uint32_t value = (condemn_reasons_condition & (1 << condemn_gen_reason));
        return value;
    }

    uint32_t get_reasons0()
    {
        return condemn_reasons_gen;
    }

    uint32_t get_reasons1()
    {
        return condemn_reasons_condition;
    }

#ifdef DT_LOG
    char get_gen_char (uint32_t value)
    {
        return char_gen_number[value];
    }
    char get_condition_char (uint32_t value)
    {
        return (value ? 'Y' : 'N');
    }
#endif //DT_LOG

    void print (int heap_num);
};

// Right now these are all size_t's but if you add a type that requires
// padding you should add a pragma pack here since I am firing this as
// a struct in an ETW event.
struct gc_generation_data
{
    // data recorded at the beginning of a GC
    size_t size_before; // including fragmentation.
    size_t free_list_space_before;
    size_t free_obj_space_before;
    
    // data recorded at the end of a GC
    size_t size_after;  // including fragmentation.
    size_t free_list_space_after;
    size_t free_obj_space_after;
    size_t in;
    size_t pinned_surv;
    size_t npinned_surv;
    size_t new_allocation;

    void print (int heap_num, int gen_num);
};

struct maxgen_size_increase
{
    size_t free_list_allocated;
    size_t free_list_rejected;
    size_t end_seg_allocated;
    size_t condemned_allocated;
    size_t pinned_allocated;
    size_t pinned_allocated_advance;
    uint32_t running_free_list_efficiency;
};

// The following indicates various mechanisms and one value
// related to each one. Each value has its corresponding string
// representation so if you change the enum's, make sure you
// also add its string form.

// Note that if we are doing a gen1 GC, we won't 
// really expand the heap if we are reusing, but
// we'll record the can_expand_into_p result here.
enum gc_heap_expand_mechanism
{
    expand_reuse_normal = 0,
    expand_reuse_bestfit = 1,
    expand_new_seg_ep = 2, // new seg with ephemeral promotion
    expand_new_seg = 3,
    expand_no_memory = 4, // we can't get a new seg.
    expand_next_full_gc = 5, 
    max_expand_mechanisms_count = 6
};

#ifdef DT_LOG
static char* str_heap_expand_mechanisms[] = 
{
    "reused seg with normal fit",
    "reused seg with best fit",
    "expand promoting eph",
    "expand with a new seg",
    "no memory for a new seg",
    "expand in next full GC"
};
#endif //DT_LOG

enum gc_heap_compact_reason
{
    compact_low_ephemeral = 0,
    compact_high_frag = 1,
    compact_no_gaps = 2,
    compact_loh_forced = 3,
    compact_last_gc = 4,
    compact_induced_compacting = 5,
    compact_fragmented_gen0 = 6, 
    compact_high_mem_load = 7, 
    compact_high_mem_frag = 8, 
    compact_vhigh_mem_frag = 9,
    compact_no_gc_mode = 10,
    max_compact_reasons_count = 11
};

#ifndef DACCESS_COMPILE
static BOOL gc_heap_compact_reason_mandatory_p[] =
{
    TRUE, //compact_low_ephemeral = 0,
    FALSE, //compact_high_frag = 1,
    TRUE, //compact_no_gaps = 2,
    TRUE, //compact_loh_forced = 3,
    TRUE, //compact_last_gc = 4
    TRUE, //compact_induced_compacting = 5,
    FALSE, //compact_fragmented_gen0 = 6, 
    FALSE, //compact_high_mem_load = 7, 
    TRUE, //compact_high_mem_frag = 8, 
    TRUE, //compact_vhigh_mem_frag = 9,
    TRUE //compact_no_gc_mode = 10
};

static BOOL gc_expand_mechanism_mandatory_p[] =
{
    FALSE, //expand_reuse_normal = 0,
    TRUE, //expand_reuse_bestfit = 1,
    FALSE, //expand_new_seg_ep = 2, // new seg with ephemeral promotion
    TRUE, //expand_new_seg = 3,
    FALSE, //expand_no_memory = 4, // we can't get a new seg.
    TRUE //expand_next_full_gc = 5
};
#endif //!DACCESS_COMPILE

#ifdef DT_LOG
static char* str_heap_compact_reasons[] = 
{
    "low on ephemeral space",
    "high fragmentation",
    "couldn't allocate gaps",
    "user specfied compact LOH",
    "last GC before OOM",
    "induced compacting GC",
    "fragmented gen0 (ephemeral GC)", 
    "high memory load (ephemeral GC)",
    "high memory load and frag",
    "very high memory load and frag",
    "no gc mode"
};
#endif //DT_LOG

enum gc_mechanism_per_heap
{
    gc_heap_expand,
    gc_heap_compact,
    max_mechanism_per_heap
};

enum gc_mechanism_bit_per_heap
{
    gc_mark_list_bit = 0,
    gc_demotion_bit = 1, 
    max_gc_mechanism_bits_count = 2
};

#ifdef DT_LOG
struct gc_mechanism_descr
{
    char* name;
    char** descr;
};

static gc_mechanism_descr gc_mechanisms_descr[max_mechanism_per_heap] =
{
    {"expanded heap ", str_heap_expand_mechanisms},
    {"compacted because of ", str_heap_compact_reasons}
};
#endif //DT_LOG

// Get the 0-based index of the most-significant bit in the value.
// Returns -1 if the input value is zero (i.e. has no set bits).
int index_of_highest_set_bit (size_t value);

#define mechanism_mask (1 << (sizeof (uint32_t) * 8 - 1))
// interesting per heap data we want to record for each GC.
class gc_history_per_heap
{
public:
    gc_generation_data gen_data[max_generation+2]; 
    maxgen_size_increase maxgen_size_info;
    gen_to_condemn_tuning gen_to_condemn_reasons;

    // The mechanisms data is compacted in the following way:
    // most significant bit indicates if we did the operation.
    // the rest of the bits indicate the reason/mechanism
    // why we chose to do the operation. For example:
    // if we did a heap expansion using best fit we'd have
    // 0x80000002 for the gc_heap_expand mechanism.
    // Only one value is possible for each mechanism - meaning the 
    // values are all exclusive
    // TODO: for the config stuff I need to think more about how to represent this
    // because we might want to know all reasons (at least all mandatory ones) for 
    // compact.
    // TODO: no need to the MSB for this
    uint32_t mechanisms[max_mechanism_per_heap];

    // Each bit in this uint32_t represent if a mechanism was used or not.
    uint32_t machanism_bits;

    uint32_t heap_index; 

    size_t extra_gen0_committed;

    void set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value);

    void set_mechanism_bit (gc_mechanism_bit_per_heap mech_bit)
    {
        machanism_bits |= 1 << mech_bit;
    }

    void clear_mechanism_bit (gc_mechanism_bit_per_heap mech_bit)
    {
        machanism_bits &= ~(1 << mech_bit);
    }

    BOOL is_mechanism_bit_set (gc_mechanism_bit_per_heap mech_bit)
    {
        return (machanism_bits & (1 << mech_bit));
    }
    
    void clear_mechanism(gc_mechanism_per_heap mechanism_per_heap)
    {
        uint32_t* mechanism = &mechanisms[mechanism_per_heap];
        *mechanism = 0;
    }

    int get_mechanism (gc_mechanism_per_heap mechanism_per_heap)
    {
        uint32_t mechanism = mechanisms[mechanism_per_heap];

        if (mechanism & mechanism_mask)
        {
            int index = index_of_highest_set_bit ((size_t)(mechanism & (~mechanism_mask)));
            assert (index != -1);
            return index;
        }

        return -1;
    }

    void print();
};

// we store up to 32 boolean settings.
enum gc_global_mechanism_p
{
    global_concurrent = 0,
    global_compaction = 1,
    global_promotion = 2,
    global_demotion = 3,
    global_card_bundles = 4,
    global_elevation = 5,
    max_global_mechanisms_count
};

struct gc_history_global
{
    // We may apply other factors after we calculated gen0 budget in
    // desired_new_allocation such as equalization or smoothing so
    // record the final budget here. 
    size_t final_youngest_desired;
    uint32_t num_heaps;
    int condemned_generation;
    int gen0_reduction_count;
    gc_reason reason;
    int pause_mode;
    uint32_t mem_pressure;
    uint32_t global_mechanisms_p;

    void set_mechanism_p (gc_global_mechanism_p mechanism)
    {
        global_mechanisms_p |= (1 << mechanism);
    }

    BOOL get_mechanism_p (gc_global_mechanism_p mechanism)
    {
        return (global_mechanisms_p & (1 << mechanism));
    }

    void print();
};

#endif //__gc_record_h__