summaryrefslogtreecommitdiff
path: root/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
blob: 046d5f0dacef5f1f538a84096a427227ec328264 (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
// 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.

using System;
using System.Threading;


namespace System.Diagnostics.Tracing
{
    /// <summary>
    /// RuntimeEventSource is an EventSource that represents events emitted by the managed runtime.
    /// </summary>
    [EventSource(Guid="49592C0F-5A05-516D-AA4B-A64E02026C89", Name = "System.Runtime")]
    internal sealed class RuntimeEventSource : EventSource
    {
        private static RuntimeEventSource s_RuntimeEventSource;
        private EventCounter[] _counters;

        private enum Counter {
            GCHeapSize,
            Gen0GCCount,
            Gen1GCCount,
            Gen2GCCount,
            ExceptionCount
        }

        private Timer _timer;

        private const int EnabledPollingIntervalMilliseconds = 1000; // 1 second

        public static void Initialize()
        {
            s_RuntimeEventSource = new RuntimeEventSource();
        }
        
        private RuntimeEventSource(): base(new Guid(0x49592C0F, 0x5A05, 0x516D, 0xAA, 0x4B, 0xA6, 0x4E, 0x02, 0x02, 0x6C, 0x89), "System.Runtime", EventSourceSettings.EtwSelfDescribingEventFormat)
        {

        }

        protected override void OnEventCommand(System.Diagnostics.Tracing.EventCommandEventArgs command)
        {
            if (command.Command == EventCommand.Enable)
            {
                if (_counters == null)
                {
                    // NOTE: These counters will NOT be disposed on disable command because we may be introducing 
                    // a race condition by doing that. We still want to create these lazily so that we aren't adding
                    // overhead by at all times even when counters aren't enabled.
                    _counters = new EventCounter[] {
                        // TODO: process info counters

                        // GC info counters
                        new EventCounter("Total Memory by GC", this),
                        new EventCounter("Gen 0 GC Count", this),
                        new EventCounter("Gen 1 GC Count", this),
                        new EventCounter("Gen 2 GC Count", this),

                        new EventCounter("Exception Count", this)
                    };
                }


                // Initialize the timer, but don't set it to run.
                // The timer will be set to run each time PollForTracingCommand is called.

                // TODO: We should not need this timer once we are done settling upon a high-level design for
                // what EventCounter is capable of doing. Once that decision is made, we should be able to 
                // get rid of this.
                if (_timer == null)
                {
                    _timer = new Timer(
                        callback: new TimerCallback(PollForCounterUpdate),
                        state: null,
                        dueTime: Timeout.Infinite,
                        period: Timeout.Infinite,
                        flowExecutionContext: false);
                }
                // Trigger the first poll operation on when this EventSource is enabled
                PollForCounterUpdate(null);
            }
            else if (command.Command == EventCommand.Disable)
            {
                if (_timer != null)
                {
                    _timer.Change(Timeout.Infinite, Timeout.Infinite);  // disable the timer from running until System.Runtime is re-enabled
                }
            }
        }

        public void UpdateAllCounters()
        {
            // GC counters
            _counters[(int)Counter.GCHeapSize].WriteMetric(GC.GetTotalMemory(false));
            _counters[(int)Counter.Gen0GCCount].WriteMetric(GC.CollectionCount(0));
            _counters[(int)Counter.Gen1GCCount].WriteMetric(GC.CollectionCount(1));
            _counters[(int)Counter.Gen2GCCount].WriteMetric(GC.CollectionCount(2));
            _counters[(int)Counter.ExceptionCount].WriteMetric(Exception.GetExceptionCount());
        }

        private void PollForCounterUpdate(object state)
        {
            // TODO: Need to confirm with vancem about how to do error-handling here. 
            // This disables to timer from getting rescheduled to run, which may or may not be 
            // what we eventually want. 

            // Make sure that any transient errors don't cause the listener thread to exit.
            try
            {
                UpdateAllCounters();

                // Schedule the timer to run again.
                _timer.Change(EnabledPollingIntervalMilliseconds, Timeout.Infinite);
            }
            catch { }
        }
    }
}