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 "eventpipe.h"
7#include "eventpipeconfiguration.h"
8#include "eventpipeeventinstance.h"
9#include "eventpipeprovider.h"
10#include "eventpipesession.h"
11
12#ifdef FEATURE_PERFTRACING
13
14const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
15
16EventPipeConfiguration::EventPipeConfiguration()
17{
18 STANDARD_VM_CONTRACT;
19
20 m_enabled = false;
21 m_rundownEnabled = false;
22 m_pRundownThread = NULL;
23 m_pConfigProvider = NULL;
24 m_pSession = NULL;
25 m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
26}
27
28EventPipeConfiguration::~EventPipeConfiguration()
29{
30 CONTRACTL
31 {
32 NOTHROW;
33 GC_TRIGGERS;
34 MODE_ANY;
35 }
36 CONTRACTL_END;
37
38 if(m_pConfigProvider != NULL)
39 {
40 // This unregisters the provider, which takes a
41 // HOST_BREAKABLE lock
42 EX_TRY
43 {
44 DeleteProvider(m_pConfigProvider);
45 m_pConfigProvider = NULL;
46 }
47 EX_CATCH { }
48 EX_END_CATCH(SwallowAllExceptions);
49 }
50 if(m_pSession != NULL)
51 {
52 DeleteSession(m_pSession);
53 m_pSession = NULL;
54 }
55
56 if(m_pProviderList != NULL)
57 {
58 // We swallow exceptions here because the HOST_BREAKABLE
59 // lock may throw and this destructor gets called in throw
60 // intolerant places. If that happens the provider list will leak
61 EX_TRY
62 {
63 // Take the lock before manipulating the list.
64 CrstHolder _crst(EventPipe::GetLock());
65
66 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
67 while(pElem != NULL)
68 {
69 // We don't delete provider itself because it can be in-use
70 SListElem<EventPipeProvider*> *pCurElem = pElem;
71 pElem = m_pProviderList->GetNext(pElem);
72 delete(pCurElem);
73 }
74
75 delete(m_pProviderList);
76 }
77 EX_CATCH { }
78 EX_END_CATCH(SwallowAllExceptions);
79
80 m_pProviderList = NULL;
81 }
82}
83
84void EventPipeConfiguration::Initialize()
85{
86 CONTRACTL
87 {
88 THROWS;
89 GC_TRIGGERS;
90 MODE_ANY;
91 }
92 CONTRACTL_END;
93
94 // Create the configuration provider.
95 m_pConfigProvider = CreateProvider(SL(s_configurationProviderName), NULL, NULL);
96
97 // Create the metadata event.
98 m_pMetadataEvent = m_pConfigProvider->AddEvent(
99 0, /* eventID */
100 0, /* keywords */
101 0, /* eventVersion */
102 EventPipeEventLevel::LogAlways,
103 false); /* needStack */
104}
105
106EventPipeProvider* EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
107{
108 CONTRACTL
109 {
110 THROWS;
111 GC_TRIGGERS;
112 MODE_ANY;
113 }
114 CONTRACTL_END;
115
116 // Allocate a new provider.
117 EventPipeProvider *pProvider = new EventPipeProvider(this, providerName, pCallbackFunction, pCallbackData);
118
119 // Register the provider with the configuration system.
120 RegisterProvider(*pProvider);
121
122 return pProvider;
123}
124
125void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider)
126{
127 CONTRACTL
128 {
129 THROWS;
130 GC_TRIGGERS;
131 MODE_ANY;
132 PRECONDITION(pProvider != NULL);
133 }
134 CONTRACTL_END;
135
136 if (pProvider == NULL)
137 {
138 return;
139 }
140
141 // Unregister the provider.
142 UnregisterProvider(*pProvider);
143
144 // Free the provider itself.
145 delete(pProvider);
146}
147
148
149bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
150{
151 CONTRACTL
152 {
153 THROWS;
154 GC_TRIGGERS;
155 MODE_ANY;
156 }
157 CONTRACTL_END;
158
159 // Take the lock before manipulating the provider list.
160 CrstHolder _crst(EventPipe::GetLock());
161
162 // See if we've already registered this provider.
163 EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName());
164 if(pExistingProvider != NULL)
165 {
166 return false;
167 }
168
169 // The provider list should be non-NULL, but can be NULL on shutdown.
170 if (m_pProviderList != NULL)
171 {
172 // The provider has not been registered, so register it.
173 m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
174 }
175
176 // Set the provider configuration and enable it if it has been requested by a session.
177 if(m_pSession != NULL)
178 {
179 EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, &provider);
180 if(pSessionProvider != NULL)
181 {
182 provider.SetConfiguration(
183 true /* providerEnabled */,
184 pSessionProvider->GetKeywords(),
185 pSessionProvider->GetLevel(),
186 pSessionProvider->GetFilterData());
187 }
188 }
189
190 return true;
191}
192
193bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
194{
195 CONTRACTL
196 {
197 THROWS;
198 GC_TRIGGERS;
199 MODE_ANY;
200 }
201 CONTRACTL_END;
202
203 // Take the lock before manipulating the provider list.
204 CrstHolder _crst(EventPipe::GetLock());
205
206 // The provider list should be non-NULL, but can be NULL on shutdown.
207 if (m_pProviderList != NULL)
208 {
209 // Find the provider.
210 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
211 while(pElem != NULL)
212 {
213 if(pElem->GetValue() == &provider)
214 {
215 break;
216 }
217
218 pElem = m_pProviderList->GetNext(pElem);
219 }
220
221 // If we found the provider, remove it.
222 if(pElem != NULL)
223 {
224 if(m_pProviderList->FindAndRemove(pElem) != NULL)
225 {
226 delete(pElem);
227 return true;
228 }
229 }
230 }
231
232 return false;
233}
234
235EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName)
236{
237 CONTRACTL
238 {
239 THROWS;
240 GC_NOTRIGGER;
241 MODE_ANY;
242 }
243 CONTRACTL_END;
244
245 // Take the lock before touching the provider list to ensure no one tries to
246 // modify the list.
247 CrstHolder _crst(EventPipe::GetLock());
248
249 return GetProviderNoLock(providerName);
250}
251
252EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
253{
254 CONTRACTL
255 {
256 THROWS;
257 GC_NOTRIGGER;
258 MODE_ANY;
259 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
260 }
261 CONTRACTL_END;
262
263 // The provider list should be non-NULL, but can be NULL on shutdown.
264 if (m_pProviderList != NULL)
265 {
266 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
267 while(pElem != NULL)
268 {
269 EventPipeProvider *pProvider = pElem->GetValue();
270 if(pProvider->GetProviderName().Equals(providerName))
271 {
272 return pProvider;
273 }
274
275 pElem = m_pProviderList->GetNext(pElem);
276 }
277 }
278
279 return NULL;
280}
281
282EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
283{
284 CONTRACTL
285 {
286 THROWS;
287 GC_NOTRIGGER;
288 MODE_ANY;
289 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
290 }
291 CONTRACTL_END;
292
293 EventPipeSessionProvider *pRet = NULL;
294 if(pSession != NULL)
295 {
296 pRet = pSession->GetSessionProvider(pProvider);
297 }
298 return pRet;
299}
300
301size_t EventPipeConfiguration::GetCircularBufferSize() const
302{
303 LIMITED_METHOD_CONTRACT;
304
305 size_t ret = 0;
306 if(m_pSession != NULL)
307 {
308 ret = m_pSession->GetCircularBufferSize();
309 }
310 return ret;
311}
312
313EventPipeSession* EventPipeConfiguration::CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds)
314{
315 CONTRACTL
316 {
317 THROWS;
318 GC_NOTRIGGER;
319 MODE_ANY;
320 }
321 CONTRACTL_END;
322
323 return new EventPipeSession(sessionType, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds);
324}
325
326void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
327{
328 CONTRACTL
329 {
330 THROWS;
331 GC_NOTRIGGER;
332 MODE_ANY;
333 PRECONDITION(pSession != NULL);
334 PRECONDITION(m_enabled == false);
335 }
336 CONTRACTL_END;
337
338 // TODO: Multiple session support will require individual enabled bits.
339 if(pSession != NULL && !m_enabled)
340 {
341 delete(pSession);
342 }
343}
344
345void EventPipeConfiguration::Enable(EventPipeSession *pSession)
346{
347 CONTRACTL
348 {
349 THROWS;
350 GC_TRIGGERS;
351 MODE_ANY;
352 PRECONDITION(pSession != NULL);
353 // Lock must be held by EventPipe::Enable.
354 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
355 }
356 CONTRACTL_END;
357
358 m_pSession = pSession;
359 m_enabled = true;
360
361 // The provider list should be non-NULL, but can be NULL on shutdown.
362 if (m_pProviderList != NULL)
363 {
364 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
365 while(pElem != NULL)
366 {
367 EventPipeProvider *pProvider = pElem->GetValue();
368
369 // Enable the provider if it has been configured.
370 EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, pProvider);
371 if(pSessionProvider != NULL)
372 {
373 pProvider->SetConfiguration(
374 true /* providerEnabled */,
375 pSessionProvider->GetKeywords(),
376 pSessionProvider->GetLevel(),
377 pSessionProvider->GetFilterData());
378 }
379
380 pElem = m_pProviderList->GetNext(pElem);
381 }
382 }
383}
384
385void EventPipeConfiguration::Disable(EventPipeSession *pSession)
386{
387 CONTRACTL
388 {
389 THROWS;
390 GC_TRIGGERS;
391 MODE_ANY;
392 // TODO: Multiple session support will require that the session be specified.
393 PRECONDITION(pSession != NULL);
394 PRECONDITION(pSession == m_pSession);
395 // Lock must be held by EventPipe::Disable.
396 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
397 }
398 CONTRACTL_END;
399
400 // The provider list should be non-NULL, but can be NULL on shutdown.
401 if (m_pProviderList != NULL)
402 {
403 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
404 while(pElem != NULL)
405 {
406 EventPipeProvider *pProvider = pElem->GetValue();
407 pProvider->SetConfiguration(
408 false /* providerEnabled */,
409 0 /* keywords */,
410 EventPipeEventLevel::Critical /* level */,
411 NULL /* filterData */);
412
413 pElem = m_pProviderList->GetNext(pElem);
414 }
415 }
416
417 m_enabled = false;
418 m_rundownEnabled = false;
419 m_pRundownThread = NULL;
420 m_pSession = NULL;
421}
422
423bool EventPipeConfiguration::Enabled() const
424{
425 LIMITED_METHOD_CONTRACT;
426 return m_enabled;
427}
428
429bool EventPipeConfiguration::RundownEnabled() const
430{
431 LIMITED_METHOD_CONTRACT;
432 return m_rundownEnabled;
433}
434
435void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession)
436{
437 CONTRACTL
438 {
439 THROWS;
440 GC_TRIGGERS;
441 MODE_ANY;
442 PRECONDITION(pSession != NULL);
443 // Lock must be held by EventPipe::Disable.
444 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
445 }
446 CONTRACTL_END;
447
448 // Build the rundown configuration.
449 _ASSERTE(m_pSession == NULL);
450
451 // Enable rundown and keep track of the rundown thread.
452 // TODO: Move this into EventPipeSession once Enable takes an EventPipeSession object.
453 m_pRundownThread = GetThread();
454 _ASSERTE(m_pRundownThread != NULL);
455 m_rundownEnabled = true;
456
457 // Enable tracing.
458 Enable(pSession);
459}
460
461EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
462{
463 CONTRACTL
464 {
465 THROWS;
466 GC_NOTRIGGER;
467 MODE_ANY;
468 }
469 CONTRACTL_END;
470
471 // The payload of the event should contain:
472 // - Metadata ID
473 // - GUID ProviderID.
474 // - Optional event description payload.
475
476 // Calculate the size of the event.
477 EventPipeEvent &sourceEvent = *sourceInstance.GetEvent();
478 const SString &providerName = sourceEvent.GetProvider()->GetProviderName();
479 BYTE *pPayloadData = sourceEvent.GetMetadata();
480 unsigned int payloadLength = sourceEvent.GetMetadataLength();
481 unsigned int providerNameLength = (providerName.GetCount() + 1) * sizeof(WCHAR);
482 unsigned int instancePayloadSize = sizeof(metadataId) + providerNameLength + payloadLength;
483
484 // Allocate the payload.
485 BYTE *pInstancePayload = new BYTE[instancePayloadSize];
486
487 // Fill the buffer with the payload.
488 BYTE *currentPtr = pInstancePayload;
489
490 memcpy(currentPtr, &metadataId, sizeof(metadataId));
491 currentPtr += sizeof(metadataId);
492
493 memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength);
494 currentPtr += providerNameLength;
495
496 // Write the incoming payload data.
497 memcpy(currentPtr, pPayloadData, payloadLength);
498
499 // Construct the event instance.
500 EventPipeEventInstance *pInstance = new EventPipeEventInstance(
501 *EventPipe::s_pSession,
502 *m_pMetadataEvent,
503 GetCurrentThreadId(),
504 pInstancePayload,
505 instancePayloadSize,
506 NULL /* pActivityId */,
507 NULL /* pRelatedActivityId */);
508
509 // Set the timestamp to match the source event, because the metadata event
510 // will be emitted right before the source event.
511 pInstance->SetTimeStamp(*sourceInstance.GetTimeStamp());
512
513 return pInstance;
514}
515
516void EventPipeConfiguration::DeleteDeferredProviders()
517{
518 CONTRACTL
519 {
520 THROWS;
521 GC_TRIGGERS;
522 MODE_ANY;
523 // Lock must be held by EventPipe::Disable.
524 PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
525
526 }
527 CONTRACTL_END;
528
529 // The provider list should be non-NULL, but can be NULL on shutdown.
530 if (m_pProviderList != NULL)
531 {
532 SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
533 while(pElem != NULL)
534 {
535 EventPipeProvider *pProvider = pElem->GetValue();
536 pElem = m_pProviderList->GetNext(pElem);
537 if(pProvider->GetDeleteDeferred())
538 {
539 DeleteProvider(pProvider);
540 }
541 }
542 }
543}
544#endif // FEATURE_PERFTRACING
545