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#include "common.h"
6#include "eventpipebuffer.h"
7#include "eventpipeblock.h"
8#include "eventpipeconfiguration.h"
9#include "eventpipefile.h"
10#include "sampleprofiler.h"
11
12#ifdef FEATURE_PERFTRACING
13
14EventPipeFile::EventPipeFile(
15 SString &outputFilePath)
16{
17 CONTRACTL
18 {
19 THROWS;
20 GC_TRIGGERS;
21 MODE_ANY;
22 }
23 CONTRACTL_END;
24
25 SetObjectVersion(3);
26 SetMinReaderVersion(0);
27
28 m_pBlock = new EventPipeBlock(100 * 1024);
29
30 // File start time information.
31 GetSystemTime(&m_fileOpenSystemTime);
32 QueryPerformanceCounter(&m_fileOpenTimeStamp);
33 QueryPerformanceFrequency(&m_timeStampFrequency);
34
35 m_pointerSize = TARGET_POINTER_SIZE;
36
37 m_currentProcessId = GetCurrentProcessId();
38
39 SYSTEM_INFO sysinfo = {};
40 GetSystemInfo(&sysinfo);
41 m_numberOfProcessors = sysinfo.dwNumberOfProcessors;
42
43 m_samplingRateInNs = SampleProfiler::GetSamplingRate();
44
45 // Create the file stream and write the header.
46 m_pSerializer = new FastSerializer(outputFilePath);
47
48 m_serializationLock.Init(LOCK_TYPE_DEFAULT);
49 m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>();
50
51 // Start and 0 - The value is always incremented prior to use, so the first ID will be 1.
52 m_metadataIdCounter = 0;
53
54 // Write the first object to the file.
55 m_pSerializer->WriteObject(this);
56}
57
58EventPipeFile::~EventPipeFile()
59{
60 CONTRACTL
61 {
62 NOTHROW;
63 GC_TRIGGERS;
64 MODE_ANY;
65 }
66 CONTRACTL_END;
67
68 if (m_pBlock != NULL && m_pSerializer != NULL)
69 {
70 WriteEnd();
71 }
72
73 if (m_pBlock != NULL)
74 {
75 delete(m_pBlock);
76 m_pBlock = NULL;
77 }
78
79 if(m_pSerializer != NULL)
80 {
81 delete(m_pSerializer);
82 m_pSerializer = NULL;
83 }
84}
85
86void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
87{
88 CONTRACTL
89 {
90 THROWS;
91 GC_NOTRIGGER;
92 MODE_ANY;
93 }
94 CONTRACTL_END;
95
96 // Check to see if we've seen this event type before.
97 // If not, then write the event metadata to the event stream first.
98 unsigned int metadataId = GetMetadataId(*instance.GetEvent());
99 if(metadataId == 0)
100 {
101 metadataId = GenerateMetadataId();
102
103 EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId);
104
105 WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event.
106
107 SaveMetadataId(*instance.GetEvent(), metadataId);
108
109 delete[] (pMetadataInstance->GetData());
110 delete (pMetadataInstance);
111 }
112
113 WriteToBlock(instance, metadataId);
114}
115
116void EventPipeFile::WriteEnd()
117{
118 CONTRACTL
119 {
120 NOTHROW;
121 GC_NOTRIGGER;
122 MODE_ANY;
123 }
124 CONTRACTL_END;
125
126 m_pSerializer->WriteObject(m_pBlock); // we write current block to the disk, whether it's full or not
127
128 m_pBlock->Clear();
129
130 // "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read."
131 // see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more
132 m_pSerializer->WriteTag(FastSerializerTags::NullReference);
133}
134
135void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId)
136{
137 CONTRACTL
138 {
139 THROWS;
140 GC_NOTRIGGER;
141 MODE_ANY;
142 }
143 CONTRACTL_END;
144
145 instance.SetMetadataId(metadataId);
146
147 if (m_pBlock->WriteEvent(instance))
148 {
149 return; // the block is not full, we added the event and continue
150 }
151
152 // we can't write this event to the current block (it's full)
153 // so we write what we have in the block to the serializer
154 m_pSerializer->WriteObject(m_pBlock);
155
156 m_pBlock->Clear();
157
158 bool result = m_pBlock->WriteEvent(instance);
159
160 _ASSERTE(result == true); // we should never fail to add event to a clear block (if we do the max size is too small)
161}
162
163unsigned int EventPipeFile::GenerateMetadataId()
164{
165 CONTRACTL
166 {
167 NOTHROW;
168 GC_NOTRIGGER;
169 MODE_ANY;
170 }
171 CONTRACTL_END;
172
173 // PAL does not support 32 bit InterlockedIncrement, so we are using the LONG version and cast to int
174 // https://github.com/dotnet/coreclr/blob/master/src/pal/inc/pal.h#L4159
175 // it's ok because the metadataId will never be bigger than 32 bit
176 return (unsigned int)InterlockedIncrement(&m_metadataIdCounter);
177}
178
179unsigned int EventPipeFile::GetMetadataId(EventPipeEvent &event)
180{
181 CONTRACTL
182 {
183 NOTHROW;
184 GC_NOTRIGGER;
185 MODE_ANY;
186 }
187 CONTRACTL_END;
188
189 unsigned int metadataId;
190 if(m_pMetadataIds->Lookup(&event, &metadataId))
191 {
192 _ASSERTE(metadataId != 0);
193 return metadataId;
194 }
195
196 return 0;
197}
198
199void EventPipeFile::SaveMetadataId(EventPipeEvent &event, unsigned int metadataId)
200{
201 CONTRACTL
202 {
203 THROWS;
204 GC_NOTRIGGER;
205 MODE_ANY;
206 PRECONDITION(metadataId > 0);
207 }
208 CONTRACTL_END;
209
210 // If a pre-existing metadata label exists, remove it.
211 unsigned int oldId;
212 if(m_pMetadataIds->Lookup(&event, &oldId))
213 {
214 m_pMetadataIds->Remove(&event);
215 }
216
217 // Add the metadata label.
218 m_pMetadataIds->Add(&event, metadataId);
219}
220
221#endif // FEATURE_PERFTRACING
222