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 | |
14 | const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration" ); |
15 | |
16 | EventPipeConfiguration::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 | |
28 | EventPipeConfiguration::~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 | |
84 | void 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 | |
106 | EventPipeProvider* 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 | |
125 | void 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 | |
149 | bool 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 | |
193 | bool 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 | |
235 | EventPipeProvider* 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 | |
252 | EventPipeProvider* 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 | |
282 | EventPipeSessionProvider* 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 | |
301 | size_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 | |
313 | EventPipeSession* 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 | |
326 | void 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 | |
345 | void 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 | |
385 | void 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 | |
423 | bool EventPipeConfiguration::Enabled() const |
424 | { |
425 | LIMITED_METHOD_CONTRACT; |
426 | return m_enabled; |
427 | } |
428 | |
429 | bool EventPipeConfiguration::RundownEnabled() const |
430 | { |
431 | LIMITED_METHOD_CONTRACT; |
432 | return m_rundownEnabled; |
433 | } |
434 | |
435 | void 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 | |
461 | EventPipeEventInstance* 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 | |
516 | void 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 | |