| 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 "eventpipebuffer.h" | 
| 9 | #include "eventpipebuffermanager.h" | 
| 10 |  | 
| 11 | #ifdef FEATURE_PERFTRACING | 
| 12 |  | 
| 13 | EventPipeBufferManager::EventPipeBufferManager() | 
| 14 | { | 
| 15 |     CONTRACTL | 
| 16 |     { | 
| 17 |         THROWS; | 
| 18 |         GC_NOTRIGGER; | 
| 19 |         MODE_ANY; | 
| 20 |     } | 
| 21 |     CONTRACTL_END; | 
| 22 |  | 
| 23 |     m_pPerThreadBufferList = new SList<SListElem<EventPipeBufferList*>>(); | 
| 24 |     m_sizeOfAllBuffers = 0; | 
| 25 |     m_lock.Init(LOCK_TYPE_DEFAULT); | 
| 26 |  | 
| 27 | #ifdef _DEBUG | 
| 28 |     m_numBuffersAllocated = 0; | 
| 29 |     m_numBuffersStolen = 0; | 
| 30 |     m_numBuffersLeaked = 0; | 
| 31 |     m_numEventsStored = 0; | 
| 32 |     m_numEventsDropped = 0; | 
| 33 |     m_numEventsWritten = 0; | 
| 34 | #endif // _DEBUG | 
| 35 | } | 
| 36 |  | 
| 37 | EventPipeBufferManager::~EventPipeBufferManager() | 
| 38 | { | 
| 39 |     CONTRACTL | 
| 40 |     { | 
| 41 |         NOTHROW; | 
| 42 |         GC_TRIGGERS; | 
| 43 |         MODE_ANY; | 
| 44 |     } | 
| 45 |     CONTRACTL_END; | 
| 46 |  | 
| 47 |     if(m_pPerThreadBufferList != NULL) | 
| 48 |     { | 
| 49 |         SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 50 |         while(pElem != NULL) | 
| 51 |         { | 
| 52 |             SListElem<EventPipeBufferList*> *pCurElem = pElem; | 
| 53 |  | 
| 54 |             EventPipeBufferList *pThreadBufferList = pCurElem->GetValue(); | 
| 55 |             if (!pThreadBufferList->OwnedByThread()) | 
| 56 |             { | 
| 57 |                 Thread *pThread = NULL; | 
| 58 |                 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) | 
| 59 |                 { | 
| 60 |                     if (pThread->GetEventPipeBufferList() == pThreadBufferList) | 
| 61 |                     { | 
| 62 |                         pThread->SetEventPipeBufferList(NULL); | 
| 63 |                         break; | 
| 64 |                     } | 
| 65 |                 } | 
| 66 |  | 
| 67 |                 // We don't delete buffers themself because they can be in-use | 
| 68 |                 delete(pThreadBufferList); | 
| 69 |             } | 
| 70 |  | 
| 71 |             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 72 |             delete(pCurElem); | 
| 73 |         } | 
| 74 |  | 
| 75 |         delete(m_pPerThreadBufferList); | 
| 76 |         m_pPerThreadBufferList = NULL; | 
| 77 |     } | 
| 78 | } | 
| 79 |  | 
| 80 | EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(EventPipeSession &session, Thread *pThread, unsigned int requestSize) | 
| 81 | { | 
| 82 |     CONTRACTL | 
| 83 |     { | 
| 84 |         NOTHROW; | 
| 85 |         GC_NOTRIGGER; | 
| 86 |         MODE_ANY; | 
| 87 |         PRECONDITION(pThread != NULL); | 
| 88 |         PRECONDITION(requestSize > 0); | 
| 89 |     } | 
| 90 |     CONTRACTL_END; | 
| 91 |  | 
| 92 |     // Allocating a buffer requires us to take the lock. | 
| 93 |     SpinLockHolder _slh(&m_lock); | 
| 94 |  | 
| 95 |     // Determine if the requesting thread has at least one buffer. | 
| 96 |     // If not, we guarantee that each thread gets at least one (to prevent thrashing when the circular buffer size is too small). | 
| 97 |     bool allocateNewBuffer = false; | 
| 98 |     EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList(); | 
| 99 |     if(pThreadBufferList == NULL) | 
| 100 |     { | 
| 101 |         pThreadBufferList = new (nothrow) EventPipeBufferList(this); | 
| 102 |         if (pThreadBufferList == NULL) | 
| 103 |         { | 
| 104 |             return NULL; | 
| 105 |         } | 
| 106 |  | 
| 107 |         SListElem<EventPipeBufferList*> *pElem = new (nothrow) SListElem<EventPipeBufferList*>(pThreadBufferList); | 
| 108 |         if (pElem == NULL) | 
| 109 |         { | 
| 110 |             return NULL; | 
| 111 |         } | 
| 112 |  | 
| 113 |         m_pPerThreadBufferList->InsertTail(pElem); | 
| 114 |         pThread->SetEventPipeBufferList(pThreadBufferList); | 
| 115 |         allocateNewBuffer = true; | 
| 116 |     } | 
| 117 |  | 
| 118 |     // Determine if policy allows us to allocate another buffer, or if we need to steal one | 
| 119 |     // from another thread. | 
| 120 |     if(!allocateNewBuffer) | 
| 121 |     { | 
| 122 |         EventPipeConfiguration *pConfig = EventPipe::GetConfiguration(); | 
| 123 |         if(pConfig == NULL) | 
| 124 |         { | 
| 125 |             return NULL; | 
| 126 |         } | 
| 127 |  | 
| 128 |         size_t circularBufferSizeInBytes = pConfig->GetCircularBufferSize(); | 
| 129 |         if(m_sizeOfAllBuffers < circularBufferSizeInBytes) | 
| 130 |         { | 
| 131 |             // We don't worry about the fact that a new buffer could put us over the circular buffer size. | 
| 132 |             // This is OK, and we won't do it again if we actually go over. | 
| 133 |             allocateNewBuffer = true; | 
| 134 |         } | 
| 135 |     } | 
| 136 |  | 
| 137 |     // Only steal buffers from other threads if the session being written to is a | 
| 138 |     // file-based session.  Streaming sessions will simply drop events. | 
| 139 |     // TODO: Add dropped events telemetry here. | 
| 140 |     EventPipeBuffer *pNewBuffer = NULL; | 
| 141 |     if(!allocateNewBuffer && (session.GetSessionType() == EventPipeSessionType::File)) | 
| 142 |     { | 
| 143 |         // We can't allocate a new buffer. | 
| 144 |         // Find the oldest buffer, de-allocate it, and re-purpose it for this thread. | 
| 145 |  | 
| 146 |         // Find the thread that contains the oldest stealable buffer, and get its list of buffers. | 
| 147 |         EventPipeBufferList *pListToStealFrom = FindThreadToStealFrom(); | 
| 148 |         if(pListToStealFrom != NULL) | 
| 149 |         { | 
| 150 |             // Assert that the buffer we're stealing is not the only buffer in the list. | 
| 151 |             // This invariant is enforced by FindThreadToStealFrom. | 
| 152 |             _ASSERTE((pListToStealFrom->GetHead() != NULL) && (pListToStealFrom->GetHead()->GetNext() != NULL)); | 
| 153 |  | 
| 154 |             // Remove the oldest buffer from the list. | 
| 155 |             pNewBuffer = pListToStealFrom->GetAndRemoveHead(); | 
| 156 |  | 
| 157 |             // De-allocate the buffer.  We do this because buffers are variable sized | 
| 158 |             // based on how much volume is coming from the thread. | 
| 159 |             DeAllocateBuffer(pNewBuffer); | 
| 160 |             pNewBuffer = NULL; | 
| 161 |  | 
| 162 |             // Set that we want to allocate a new buffer. | 
| 163 |             allocateNewBuffer = true; | 
| 164 |  | 
| 165 | #ifdef _DEBUG | 
| 166 |             m_numBuffersStolen++; | 
| 167 | #endif // _DEBUG | 
| 168 |  | 
| 169 |         } | 
| 170 |         else | 
| 171 |         { | 
| 172 |             // This only happens when # of threads == # of buffers. | 
| 173 |             // We'll allocate one more buffer, and then this won't happen again. | 
| 174 |             allocateNewBuffer = true; | 
| 175 |         } | 
| 176 |     } | 
| 177 |  | 
| 178 |     if(allocateNewBuffer) | 
| 179 |     { | 
| 180 |         // Pick a buffer size by multiplying the base buffer size by the number of buffers already allocated for this thread. | 
| 181 |         unsigned int sizeMultiplier = pThreadBufferList->GetCount() + 1; | 
| 182 |  | 
| 183 |         // Pick the base buffer size based.  Debug builds have a smaller size to stress the allocate/steal path more. | 
| 184 |         unsigned int baseBufferSize = | 
| 185 | #ifdef _DEBUG | 
| 186 |             30 * 1024; // 30K | 
| 187 | #else | 
| 188 |             100 * 1024; // 100K | 
| 189 | #endif | 
| 190 |         unsigned int bufferSize = baseBufferSize * sizeMultiplier; | 
| 191 |  | 
| 192 |         // Make sure that buffer size >= request size so that the buffer size does not | 
| 193 |         // determine the max event size. | 
| 194 |         if(bufferSize < requestSize) | 
| 195 |         { | 
| 196 |             bufferSize = requestSize; | 
| 197 |         } | 
| 198 |  | 
| 199 |         // Don't allow the buffer size to exceed 1MB. | 
| 200 |         const unsigned int maxBufferSize = 1024 * 1024; | 
| 201 |         if(bufferSize > maxBufferSize) | 
| 202 |         { | 
| 203 |             bufferSize = maxBufferSize; | 
| 204 |         } | 
| 205 |  | 
| 206 |         // EX_TRY is used here as opposed to new (nothrow) because | 
| 207 |         // the constructor also allocates a private buffer, which | 
| 208 |         // could throw, and cannot be easily checked | 
| 209 |         EX_TRY | 
| 210 |         { | 
| 211 |             pNewBuffer = new EventPipeBuffer(bufferSize); | 
| 212 |         } | 
| 213 |         EX_CATCH | 
| 214 |         { | 
| 215 |             pNewBuffer = NULL; | 
| 216 |         } | 
| 217 |         EX_END_CATCH(SwallowAllExceptions); | 
| 218 |  | 
| 219 |         if (pNewBuffer == NULL) | 
| 220 |         { | 
| 221 |             return NULL; | 
| 222 |         } | 
| 223 |  | 
| 224 |         m_sizeOfAllBuffers += bufferSize; | 
| 225 | #ifdef _DEBUG | 
| 226 |         m_numBuffersAllocated++; | 
| 227 | #endif // _DEBUG | 
| 228 |     } | 
| 229 |  | 
| 230 |     // Set the buffer on the thread. | 
| 231 |     if(pNewBuffer != NULL) | 
| 232 |     { | 
| 233 |         pThreadBufferList->InsertTail(pNewBuffer); | 
| 234 |         return pNewBuffer; | 
| 235 |     } | 
| 236 |  | 
| 237 |     return NULL; | 
| 238 | } | 
| 239 |  | 
| 240 | EventPipeBufferList* EventPipeBufferManager::FindThreadToStealFrom() | 
| 241 | { | 
| 242 |     CONTRACTL | 
| 243 |     { | 
| 244 |         NOTHROW; | 
| 245 |         GC_NOTRIGGER; | 
| 246 |         MODE_ANY; | 
| 247 |         PRECONDITION(m_lock.OwnedByCurrentThread()); | 
| 248 |     } | 
| 249 |     CONTRACTL_END; | 
| 250 |  | 
| 251 |     // Find the thread buffer list containing the buffer whose most recent event is the oldest as long as the buffer is not | 
| 252 |     // the current buffer for the thread (e.g. it's next pointer is non-NULL). | 
| 253 |     // This means that the thread must also have multiple buffers, so that we don't steal its only buffer. | 
| 254 |     EventPipeBufferList *pOldestContainingList = NULL; | 
| 255 |  | 
| 256 |     SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 257 |     while(pElem != NULL) | 
| 258 |     { | 
| 259 |         EventPipeBufferList *pCandidate = pElem->GetValue(); | 
| 260 |  | 
| 261 |         // The current candidate has more than one buffer (otherwise it is disqualified). | 
| 262 |         if(pCandidate->GetHead()->GetNext() != NULL) | 
| 263 |         { | 
| 264 |             // If we haven't seen any candidates, this one automatically becomes the oldest candidate. | 
| 265 |             if(pOldestContainingList == NULL) | 
| 266 |             { | 
| 267 |                 pOldestContainingList = pCandidate; | 
| 268 |             } | 
| 269 |             // Otherwise, to replace the existing candidate, this candidate must have an older timestamp in its oldest buffer. | 
| 270 |             else if((pOldestContainingList->GetHead()->GetMostRecentTimeStamp().QuadPart) >  | 
| 271 |                       (pCandidate->GetHead()->GetMostRecentTimeStamp().QuadPart)) | 
| 272 |             { | 
| 273 |                 pOldestContainingList = pCandidate; | 
| 274 |             } | 
| 275 |         } | 
| 276 |  | 
| 277 |         pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 278 |     } | 
| 279 |  | 
| 280 |     return pOldestContainingList; | 
| 281 | } | 
| 282 |  | 
| 283 | void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer) | 
| 284 | { | 
| 285 |     CONTRACTL | 
| 286 |     { | 
| 287 |         NOTHROW; | 
| 288 |         GC_NOTRIGGER; | 
| 289 |         MODE_ANY; | 
| 290 |     } | 
| 291 |     CONTRACTL_END; | 
| 292 |  | 
| 293 |     if(pBuffer != NULL) | 
| 294 |     { | 
| 295 |         m_sizeOfAllBuffers -= pBuffer->GetSize(); | 
| 296 |         delete(pBuffer); | 
| 297 | #ifdef _DEBUG | 
| 298 |         m_numBuffersAllocated--; | 
| 299 | #endif // _DEBUG | 
| 300 |     } | 
| 301 | } | 
| 302 |  | 
| 303 | bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack) | 
| 304 | { | 
| 305 |     CONTRACTL | 
| 306 |     { | 
| 307 |         NOTHROW; | 
| 308 |         GC_NOTRIGGER; | 
| 309 |         MODE_ANY; | 
| 310 |         // The input thread must match the current thread because no lock is taken on the buffer. | 
| 311 |         PRECONDITION(pThread == GetThread()); | 
| 312 |     } | 
| 313 |     CONTRACTL_END; | 
| 314 |  | 
| 315 |     _ASSERTE(pThread == GetThread()); | 
| 316 |  | 
| 317 |     // Check to see an event thread was specified.  If not, then use the current thread. | 
| 318 |     if(pEventThread == NULL) | 
| 319 |     { | 
| 320 |         pEventThread = pThread; | 
| 321 |     } | 
| 322 |  | 
| 323 |     // Before we pick a buffer, make sure the event is enabled. | 
| 324 |     if(!event.IsEnabled()) | 
| 325 |     { | 
| 326 |         return false; | 
| 327 |     } | 
| 328 |  | 
| 329 |     // The event is still enabled.  Mark that the thread is now writing an event. | 
| 330 |     pThread->SetEventWriteInProgress(true); | 
| 331 |  | 
| 332 |     // Check one more time to make sure that the event is still enabled. | 
| 333 |     // We do this because we might be trying to disable tracing and free buffers, so we | 
| 334 |     // must make sure that the event is enabled after we mark that we're writing to avoid | 
| 335 |     // races with the destructing thread. | 
| 336 |     if(!event.IsEnabled()) | 
| 337 |     { | 
| 338 |         return false; | 
| 339 |     } | 
| 340 |  | 
| 341 |     // See if the thread already has a buffer to try. | 
| 342 |     bool allocNewBuffer = false; | 
| 343 |     EventPipeBuffer *pBuffer = NULL; | 
| 344 |     EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList(); | 
| 345 |     if(pThreadBufferList == NULL) | 
| 346 |     { | 
| 347 |         allocNewBuffer = true; | 
| 348 |     } | 
| 349 |     else | 
| 350 |     { | 
| 351 |         // The thread already has a buffer list.  Select the newest buffer and attempt to write into it. | 
| 352 |         pBuffer = pThreadBufferList->GetTail(); | 
| 353 |         if(pBuffer == NULL) | 
| 354 |         { | 
| 355 |             // This should never happen.  If the buffer list exists, it must contain at least one entry. | 
| 356 |             _ASSERT(!"Thread buffer list with zero entries encountered." ); | 
| 357 |             return false; | 
| 358 |         } | 
| 359 |         else | 
| 360 |         { | 
| 361 |             // Attempt to write the event to the buffer.  If this fails, we should allocate a new buffer. | 
| 362 |             allocNewBuffer = !pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack); | 
| 363 |         } | 
| 364 |     } | 
| 365 |  | 
| 366 |     // Check to see if we need to allocate a new buffer, and if so, do it here. | 
| 367 |     if(allocNewBuffer) | 
| 368 |     { | 
| 369 |         // We previously switched to preemptive mode here, however, this is not safe and can cause deadlocks. | 
| 370 |         // When a GC is started, and background threads are created (for the first BGC), a thread creation event is fired. | 
| 371 |         // When control gets here the buffer is allocated, but then the thread hangs waiting for the GC to complete | 
| 372 |         // (it was marked as started before creating threads) so that it can switch back to cooperative mode. | 
| 373 |         // However, the GC is waiting on this call to return so that it can make forward progress.  Thus it is not safe | 
| 374 |         // to switch to preemptive mode here. | 
| 375 |  | 
| 376 |         unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize(); | 
| 377 |         pBuffer = AllocateBufferForThread(session, pThread, requestSize); | 
| 378 |     } | 
| 379 |  | 
| 380 |     // Try to write the event after we allocated (or stole) a buffer. | 
| 381 |     // This is the first time if the thread had no buffers before the call to this function. | 
| 382 |     // This is the second time if this thread did have one or more buffers, but they were full. | 
| 383 |     if(allocNewBuffer && pBuffer != NULL) | 
| 384 |     { | 
| 385 |         allocNewBuffer = !pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack); | 
| 386 |     } | 
| 387 |  | 
| 388 |     // Mark that the thread is no longer writing an event. | 
| 389 |      pThread->SetEventWriteInProgress(false); | 
| 390 |  | 
| 391 | #ifdef _DEBUG | 
| 392 |     if(!allocNewBuffer) | 
| 393 |     { | 
| 394 |         InterlockedIncrement(&m_numEventsStored); | 
| 395 |     } | 
| 396 |     else | 
| 397 |     { | 
| 398 |         InterlockedIncrement(&m_numEventsDropped); | 
| 399 |     } | 
| 400 | #endif // _DEBUG | 
| 401 |     return !allocNewBuffer; | 
| 402 | } | 
| 403 |  | 
| 404 | void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp) | 
| 405 | { | 
| 406 |     CONTRACTL | 
| 407 |     { | 
| 408 |         THROWS; | 
| 409 |         GC_NOTRIGGER; | 
| 410 |         MODE_ANY; | 
| 411 |         PRECONDITION(pFile != NULL); | 
| 412 |     } | 
| 413 |     CONTRACTL_END; | 
| 414 |  | 
| 415 |     // TODO: Better version of merge sort. | 
| 416 |     // 1. Iterate through all of the threads, adding each buffer to a temporary list. | 
| 417 |     // 2. While iterating, get the lowest most recent timestamp.  This is the timestamp that we want to process up to. | 
| 418 |     // 3. Process up to the lowest most recent timestamp for the set of buffers. | 
| 419 |     // 4. When we get NULLs from each of the buffers on PopNext(), we're done. | 
| 420 |     // 5. While iterating if PopNext() == NULL && Empty() == NULL, remove the buffer from the list.  It's empty. | 
| 421 |     // 6. While iterating, grab the next lowest most recent timestamp. | 
| 422 |     // 7. Walk through the list again and look for any buffers that have a lower most recent timestamp than the next most recent timestamp. | 
| 423 |     // 8. If we find one, add it to the list and select its most recent timestamp as the lowest. | 
| 424 |     // 9. Process again (go to 3). | 
| 425 |     // 10. Continue until there are no more buffers to process. | 
| 426 |  | 
| 427 |     // Take the lock before walking the buffer list. | 
| 428 |     SpinLockHolder _slh(&m_lock); | 
| 429 |  | 
| 430 |     // Naively walk the circular buffer, writing the event stream in timestamp order. | 
| 431 |     while(true) | 
| 432 |     { | 
| 433 |         EventPipeEventInstance *pOldestInstance = NULL; | 
| 434 |         EventPipeBuffer *pOldestContainingBuffer = NULL; | 
| 435 |         EventPipeBufferList *pOldestContainingList = NULL; | 
| 436 |         SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 437 |         while(pElem != NULL) | 
| 438 |         { | 
| 439 |             EventPipeBufferList *pBufferList = pElem->GetValue(); | 
| 440 |  | 
| 441 |             // Peek the next event out of the list. | 
| 442 |             EventPipeBuffer *pContainingBuffer = NULL; | 
| 443 |             EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer); | 
| 444 |             if(pNext != NULL) | 
| 445 |             { | 
| 446 |                 // If it's the oldest event we've seen, then save it. | 
| 447 |                 if((pOldestInstance == NULL) || | 
| 448 |                    (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))  | 
| 449 |                 { | 
| 450 |                     pOldestInstance = pNext; | 
| 451 |                     pOldestContainingBuffer = pContainingBuffer; | 
| 452 |                     pOldestContainingList = pBufferList; | 
| 453 |                 } | 
| 454 |             } | 
| 455 |  | 
| 456 |             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 457 |         } | 
| 458 |  | 
| 459 |         if(pOldestInstance == NULL) | 
| 460 |         { | 
| 461 |             // We're done.  There are no more events. | 
| 462 |             break; | 
| 463 |         } | 
| 464 |  | 
| 465 |         // Write the oldest event. | 
| 466 |         pFile->WriteEvent(*pOldestInstance); | 
| 467 | #ifdef _DEBUG | 
| 468 |         m_numEventsWritten++; | 
| 469 | #endif // _DEBUG | 
| 470 |  | 
| 471 |         // Pop the event from the buffer. | 
| 472 |         pOldestContainingList->PopNextEvent(stopTimeStamp); | 
| 473 |     } | 
| 474 | } | 
| 475 |  | 
| 476 | EventPipeEventInstance* EventPipeBufferManager::GetNextEvent() | 
| 477 | { | 
| 478 |     CONTRACTL | 
| 479 |     { | 
| 480 |         NOTHROW; | 
| 481 |         GC_NOTRIGGER; | 
| 482 |         MODE_ANY; | 
| 483 |     } | 
| 484 |     CONTRACTL_END; | 
| 485 |  | 
| 486 |     // Take the lock before walking the buffer list. | 
| 487 |     SpinLockHolder _slh(&m_lock); | 
| 488 |  | 
| 489 |     // Naively walk the circular buffer, getting the event stream in timestamp order. | 
| 490 |     LARGE_INTEGER stopTimeStamp; | 
| 491 |     QueryPerformanceCounter(&stopTimeStamp); | 
| 492 |     while (true) | 
| 493 |     { | 
| 494 |         EventPipeEventInstance *pOldestInstance = NULL; | 
| 495 |         EventPipeBuffer *pOldestContainingBuffer = NULL; | 
| 496 |         EventPipeBufferList *pOldestContainingList = NULL; | 
| 497 |         SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 498 |         while (pElem != NULL) | 
| 499 |         { | 
| 500 |             EventPipeBufferList *pBufferList = pElem->GetValue(); | 
| 501 |  | 
| 502 |             // Peek the next event out of the list. | 
| 503 |             EventPipeBuffer *pContainingBuffer = NULL; | 
| 504 |             EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer); | 
| 505 |             if (pNext != NULL) | 
| 506 |             { | 
| 507 |                 // If it's the oldest event we've seen, then save it. | 
| 508 |                 if ((pOldestInstance == NULL) || | 
| 509 |                     (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart)) | 
| 510 |                 { | 
| 511 |                     pOldestInstance = pNext; | 
| 512 |                     pOldestContainingBuffer = pContainingBuffer; | 
| 513 |                     pOldestContainingList = pBufferList; | 
| 514 |                 } | 
| 515 |             } | 
| 516 |  | 
| 517 |             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 518 |         } | 
| 519 |  | 
| 520 |         if (pOldestInstance == NULL) | 
| 521 |         { | 
| 522 |             // We're done.  There are no more events. | 
| 523 |             return NULL; | 
| 524 |         } | 
| 525 |  | 
| 526 |         // Pop the event from the buffer. | 
| 527 |         pOldestContainingList->PopNextEvent(stopTimeStamp); | 
| 528 |  | 
| 529 |         // Return the oldest event that hasn't yet been processed. | 
| 530 |         return pOldestInstance; | 
| 531 |     } | 
| 532 | } | 
| 533 |  | 
| 534 | void EventPipeBufferManager::DeAllocateBuffers() | 
| 535 | { | 
| 536 |     CONTRACTL | 
| 537 |     { | 
| 538 |         NOTHROW; | 
| 539 |         GC_NOTRIGGER; | 
| 540 |         MODE_ANY; | 
| 541 |     } | 
| 542 |     CONTRACTL_END; | 
| 543 |  | 
| 544 |     _ASSERTE(EnsureConsistency()); | 
| 545 |  | 
| 546 |     // Take the thread store lock because we're going to iterate through the thread list. | 
| 547 |     { | 
| 548 |         ThreadStoreLockHolder tsl; | 
| 549 |  | 
| 550 |         // Take the buffer manager manipulation lock. | 
| 551 |         SpinLockHolder _slh(&m_lock); | 
| 552 |  | 
| 553 |         Thread *pThread = NULL; | 
| 554 |         while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) | 
| 555 |         { | 
| 556 |             // Get the thread's buffer list. | 
| 557 |             EventPipeBufferList *pBufferList = pThread->GetEventPipeBufferList(); | 
| 558 |             if(pBufferList != NULL) | 
| 559 |             { | 
| 560 |                 // Attempt to free the buffer list. | 
| 561 |                 // If the thread is using its buffer list skip it. | 
| 562 |                 // This means we will leak a single buffer, but if tracing is re-enabled, that buffer can be used again. | 
| 563 |                 if(!pThread->GetEventWriteInProgress()) | 
| 564 |                 { | 
| 565 |                     EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead(); | 
| 566 |                     while(pBuffer != NULL) | 
| 567 |                     { | 
| 568 |                         DeAllocateBuffer(pBuffer); | 
| 569 |                         pBuffer = pBufferList->GetAndRemoveHead(); | 
| 570 |                     } | 
| 571 |  | 
| 572 |                     // Remove the list entry from the per thread buffer list. | 
| 573 |                     SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 574 |                     while(pElem != NULL) | 
| 575 |                     { | 
| 576 |                         EventPipeBufferList* pEntry = pElem->GetValue(); | 
| 577 |                         if(pEntry == pBufferList) | 
| 578 |                         { | 
| 579 |                             pElem = m_pPerThreadBufferList->FindAndRemove(pElem); | 
| 580 |  | 
| 581 |                             // In DEBUG, make sure that the element was found and removed. | 
| 582 |                             _ASSERTE(pElem != NULL); | 
| 583 |  | 
| 584 |                             SListElem<EventPipeBufferList*> *pCurElem = pElem; | 
| 585 |                             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 586 |                             delete(pCurElem); | 
| 587 |                         } | 
| 588 |                         else | 
| 589 |                         { | 
| 590 |                             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 591 |                         } | 
| 592 |                     } | 
| 593 |  | 
| 594 |                     // Remove the list reference from the thread. | 
| 595 |                     pThread->SetEventPipeBufferList(NULL); | 
| 596 |  | 
| 597 |                     // Now that all of the list elements have been freed, free the list itself. | 
| 598 |                     delete(pBufferList); | 
| 599 |                     pBufferList = NULL; | 
| 600 |                 } | 
| 601 | #ifdef _DEBUG | 
| 602 |                 else | 
| 603 |                 { | 
| 604 |                     // We can't deallocate the buffers. | 
| 605 |                     m_numBuffersLeaked += pBufferList->GetCount(); | 
| 606 |                 } | 
| 607 | #endif // _DEBUG             | 
| 608 |             } | 
| 609 |         } | 
| 610 |     } | 
| 611 |  | 
| 612 |     // Now that we've walked through all of the threads, let's see if there are any other buffers | 
| 613 |     // that belonged to threads that died during tracing.  We can free these now. | 
| 614 |  | 
| 615 |     // Take the buffer manager manipulation lock | 
| 616 |     SpinLockHolder _slh(&m_lock); | 
| 617 |  | 
| 618 |     SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 619 |     while(pElem != NULL) | 
| 620 |     { | 
| 621 |         // Get the list and determine if we can free it. | 
| 622 |         EventPipeBufferList *pBufferList = pElem->GetValue(); | 
| 623 |         if(!pBufferList->OwnedByThread()) | 
| 624 |         { | 
| 625 |             // Iterate over all nodes in the list and de-allocate them. | 
| 626 |             EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead(); | 
| 627 |             while(pBuffer != NULL) | 
| 628 |             { | 
| 629 |                 DeAllocateBuffer(pBuffer); | 
| 630 |                 pBuffer = pBufferList->GetAndRemoveHead(); | 
| 631 |             } | 
| 632 |  | 
| 633 |             // Remove the buffer list from the per-thread buffer list. | 
| 634 |             pElem = m_pPerThreadBufferList->FindAndRemove(pElem); | 
| 635 |             _ASSERTE(pElem != NULL); | 
| 636 |  | 
| 637 |             SListElem<EventPipeBufferList*> *pCurElem = pElem; | 
| 638 |             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 639 |             delete(pCurElem); | 
| 640 |  | 
| 641 |             // Now that all of the list elements have been freed, free the list itself. | 
| 642 |             delete(pBufferList); | 
| 643 |             pBufferList = NULL; | 
| 644 |         } | 
| 645 |         else | 
| 646 |         { | 
| 647 |             pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 648 |         } | 
| 649 |     }  | 
| 650 | } | 
| 651 |  | 
| 652 | #ifdef _DEBUG | 
| 653 | bool EventPipeBufferManager::EnsureConsistency() | 
| 654 | { | 
| 655 |     LIMITED_METHOD_CONTRACT; | 
| 656 |  | 
| 657 |     SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead(); | 
| 658 |     while(pElem != NULL) | 
| 659 |     { | 
| 660 |         EventPipeBufferList *pBufferList = pElem->GetValue(); | 
| 661 |  | 
| 662 |         _ASSERTE(pBufferList->EnsureConsistency()); | 
| 663 |  | 
| 664 |         pElem = m_pPerThreadBufferList->GetNext(pElem); | 
| 665 |     } | 
| 666 |  | 
| 667 |     return true; | 
| 668 | } | 
| 669 | #endif // _DEBUG | 
| 670 |  | 
| 671 | EventPipeBufferList::EventPipeBufferList(EventPipeBufferManager *pManager) | 
| 672 | { | 
| 673 |     LIMITED_METHOD_CONTRACT; | 
| 674 |  | 
| 675 |     m_pManager = pManager; | 
| 676 |     m_pHeadBuffer = NULL; | 
| 677 |     m_pTailBuffer = NULL; | 
| 678 |     m_bufferCount = 0; | 
| 679 |     m_pReadBuffer = NULL; | 
| 680 |     m_ownedByThread = true; | 
| 681 |  | 
| 682 | #ifdef _DEBUG | 
| 683 |     m_pCreatingThread = GetThread(); | 
| 684 | #endif // _DEBUG | 
| 685 | } | 
| 686 |  | 
| 687 | EventPipeBuffer* EventPipeBufferList::GetHead() | 
| 688 | { | 
| 689 |     LIMITED_METHOD_CONTRACT; | 
| 690 |  | 
| 691 |     return m_pHeadBuffer; | 
| 692 | } | 
| 693 |  | 
| 694 | EventPipeBuffer* EventPipeBufferList::GetTail() | 
| 695 | { | 
| 696 |     LIMITED_METHOD_CONTRACT; | 
| 697 |  | 
| 698 |     return m_pTailBuffer; | 
| 699 | } | 
| 700 |  | 
| 701 | void EventPipeBufferList::InsertTail(EventPipeBuffer *pBuffer) | 
| 702 | { | 
| 703 |     CONTRACTL | 
| 704 |     { | 
| 705 |         NOTHROW; | 
| 706 |         GC_NOTRIGGER; | 
| 707 |         MODE_ANY; | 
| 708 |         PRECONDITION(pBuffer != NULL); | 
| 709 |     } | 
| 710 |     CONTRACTL_END; | 
| 711 |  | 
| 712 |     _ASSERTE(EnsureConsistency()); | 
| 713 |  | 
| 714 |     // Ensure that the input buffer didn't come from another list that was improperly cleaned up. | 
| 715 |     _ASSERTE((pBuffer->GetNext() == NULL) && (pBuffer->GetPrevious() == NULL)); | 
| 716 |  | 
| 717 |     // First node in the list. | 
| 718 |     if(m_pTailBuffer == NULL) | 
| 719 |     { | 
| 720 |         m_pHeadBuffer = m_pTailBuffer = pBuffer; | 
| 721 |     } | 
| 722 |     else | 
| 723 |     { | 
| 724 |         // Set links between the old and new tail nodes. | 
| 725 |         m_pTailBuffer->SetNext(pBuffer); | 
| 726 |         pBuffer->SetPrevious(m_pTailBuffer); | 
| 727 |  | 
| 728 |         // Set the new tail node. | 
| 729 |         m_pTailBuffer = pBuffer; | 
| 730 |     } | 
| 731 |  | 
| 732 |     m_bufferCount++; | 
| 733 |  | 
| 734 |     _ASSERTE(EnsureConsistency()); | 
| 735 | } | 
| 736 |  | 
| 737 | EventPipeBuffer* EventPipeBufferList::GetAndRemoveHead() | 
| 738 | { | 
| 739 |     CONTRACTL | 
| 740 |     { | 
| 741 |         NOTHROW; | 
| 742 |         GC_NOTRIGGER; | 
| 743 |         MODE_ANY; | 
| 744 |     } | 
| 745 |     CONTRACTL_END; | 
| 746 |  | 
| 747 |     _ASSERTE(EnsureConsistency()); | 
| 748 |  | 
| 749 |     EventPipeBuffer *pRetBuffer = NULL; | 
| 750 |     if(m_pHeadBuffer != NULL) | 
| 751 |     { | 
| 752 |         // Save the head node. | 
| 753 |         pRetBuffer = m_pHeadBuffer; | 
| 754 |  | 
| 755 |         // Set the new head node. | 
| 756 |         m_pHeadBuffer = m_pHeadBuffer->GetNext(); | 
| 757 |  | 
| 758 |         // Update the head node's previous pointer. | 
| 759 |         if(m_pHeadBuffer != NULL) | 
| 760 |         { | 
| 761 |             m_pHeadBuffer->SetPrevious(NULL); | 
| 762 |         } | 
| 763 |         else | 
| 764 |         { | 
| 765 |             // We just removed the last buffer from the list. | 
| 766 |             // Make sure both head and tail pointers are NULL. | 
| 767 |             m_pTailBuffer = NULL; | 
| 768 |         } | 
| 769 |  | 
| 770 |         // Clear the next pointer of the old head node. | 
| 771 |         pRetBuffer->SetNext(NULL); | 
| 772 |  | 
| 773 |         // Ensure that the old head node has no dangling references. | 
| 774 |         _ASSERTE((pRetBuffer->GetNext() == NULL) && (pRetBuffer->GetPrevious() == NULL)); | 
| 775 |  | 
| 776 |         // Decrement the count of buffers in the list. | 
| 777 |         m_bufferCount--; | 
| 778 |     } | 
| 779 |  | 
| 780 |     _ASSERTE(EnsureConsistency()); | 
| 781 |  | 
| 782 |     return pRetBuffer; | 
| 783 | } | 
| 784 |  | 
| 785 | unsigned int EventPipeBufferList::GetCount() const | 
| 786 | { | 
| 787 |     LIMITED_METHOD_CONTRACT; | 
| 788 |  | 
| 789 |     return m_bufferCount; | 
| 790 | } | 
| 791 |  | 
| 792 | EventPipeEventInstance* EventPipeBufferList::PeekNextEvent(LARGE_INTEGER beforeTimeStamp, EventPipeBuffer **pContainingBuffer) | 
| 793 | { | 
| 794 |     CONTRACTL | 
| 795 |     { | 
| 796 |         NOTHROW; | 
| 797 |         GC_NOTRIGGER; | 
| 798 |         MODE_ANY; | 
| 799 |     } | 
| 800 |     CONTRACTL_END; | 
| 801 |  | 
| 802 |     // Get the current read buffer. | 
| 803 |     // If it's not set, start with the head buffer. | 
| 804 |     if(m_pReadBuffer == NULL) | 
| 805 |     { | 
| 806 |         m_pReadBuffer = m_pHeadBuffer; | 
| 807 |     } | 
| 808 |  | 
| 809 |     // If the read buffer is still NULL, then this list contains no buffers. | 
| 810 |     if(m_pReadBuffer == NULL) | 
| 811 |     { | 
| 812 |         return NULL; | 
| 813 |     } | 
| 814 |  | 
| 815 |     // Get the next event in the buffer. | 
| 816 |     EventPipeEventInstance *pNext = m_pReadBuffer->PeekNext(beforeTimeStamp); | 
| 817 |  | 
| 818 |     // If the next event is NULL, then go to the next buffer. | 
| 819 |     if(pNext == NULL) | 
| 820 |     { | 
| 821 |         m_pReadBuffer = m_pReadBuffer->GetNext(); | 
| 822 |         if(m_pReadBuffer != NULL) | 
| 823 |         { | 
| 824 |             pNext = m_pReadBuffer->PeekNext(beforeTimeStamp); | 
| 825 |         } | 
| 826 |     } | 
| 827 |  | 
| 828 |     // Set the containing buffer. | 
| 829 |     if(pNext != NULL && pContainingBuffer != NULL) | 
| 830 |     { | 
| 831 |         *pContainingBuffer = m_pReadBuffer; | 
| 832 |     } | 
| 833 |  | 
| 834 |     // Make sure pContainingBuffer is properly set. | 
| 835 |     _ASSERTE((pNext == NULL) || (pNext != NULL && pContainingBuffer == NULL) || (pNext != NULL && *pContainingBuffer == m_pReadBuffer)); | 
| 836 |     return pNext; | 
| 837 | } | 
| 838 |  | 
| 839 | EventPipeEventInstance* EventPipeBufferList::PopNextEvent(LARGE_INTEGER beforeTimeStamp) | 
| 840 | { | 
| 841 |     CONTRACTL | 
| 842 |     { | 
| 843 |         NOTHROW; | 
| 844 |         GC_NOTRIGGER; | 
| 845 |         MODE_ANY; | 
| 846 |     } | 
| 847 |     CONTRACTL_END; | 
| 848 |  | 
| 849 |     // Get the next event. | 
| 850 |     EventPipeBuffer *pContainingBuffer = NULL; | 
| 851 |     EventPipeEventInstance *pNext = PeekNextEvent(beforeTimeStamp, &pContainingBuffer); | 
| 852 |  | 
| 853 |     // Check to see if we need to clean-up the buffer that contained the previously popped event. | 
| 854 |     if(pContainingBuffer->GetPrevious() != NULL) | 
| 855 |     { | 
| 856 |             // Remove the previous node.  The previous node should always be the head node. | 
| 857 |             EventPipeBuffer *pRemoved = GetAndRemoveHead(); | 
| 858 |             _ASSERTE(pRemoved != pContainingBuffer); | 
| 859 |             _ASSERTE(pContainingBuffer == GetHead()); | 
| 860 |  | 
| 861 |             // De-allocate the buffer. | 
| 862 |             m_pManager->DeAllocateBuffer(pRemoved); | 
| 863 |     } | 
| 864 |  | 
| 865 |     // If the event is non-NULL, pop it. | 
| 866 |     if(pNext != NULL && pContainingBuffer != NULL) | 
| 867 |     { | 
| 868 |         pContainingBuffer->PopNext(beforeTimeStamp); | 
| 869 |     } | 
| 870 |  | 
| 871 |     return pNext; | 
| 872 | } | 
| 873 |  | 
| 874 | bool EventPipeBufferList::OwnedByThread() | 
| 875 | { | 
| 876 |     LIMITED_METHOD_CONTRACT; | 
| 877 |     return m_ownedByThread; | 
| 878 | } | 
| 879 |  | 
| 880 | void EventPipeBufferList::SetOwnedByThread(bool value) | 
| 881 | { | 
| 882 |     LIMITED_METHOD_CONTRACT; | 
| 883 |     m_ownedByThread = value; | 
| 884 | } | 
| 885 |  | 
| 886 | #ifdef _DEBUG | 
| 887 | Thread* EventPipeBufferList::GetThread() | 
| 888 | { | 
| 889 |     LIMITED_METHOD_CONTRACT; | 
| 890 |  | 
| 891 |     return m_pCreatingThread; | 
| 892 | } | 
| 893 |  | 
| 894 | bool EventPipeBufferList::EnsureConsistency() | 
| 895 | { | 
| 896 |     CONTRACTL | 
| 897 |     { | 
| 898 |         NOTHROW; | 
| 899 |         GC_NOTRIGGER; | 
| 900 |         MODE_ANY; | 
| 901 |     } | 
| 902 |     CONTRACTL_END; | 
| 903 |  | 
| 904 |     // Either the head and tail nodes are both NULL or both are non-NULL. | 
| 905 |     _ASSERTE((m_pHeadBuffer == NULL && m_pTailBuffer == NULL) || (m_pHeadBuffer != NULL && m_pTailBuffer != NULL)); | 
| 906 |  | 
| 907 |     // If the list is NULL, check the count and return. | 
| 908 |     if(m_pHeadBuffer == NULL) | 
| 909 |     { | 
| 910 |         _ASSERTE(m_bufferCount == 0); | 
| 911 |         return true; | 
| 912 |     } | 
| 913 |  | 
| 914 |     // If the list is non-NULL, walk the list forward until we get to the end. | 
| 915 |     unsigned int nodeCount = (m_pHeadBuffer != NULL) ? 1 : 0; | 
| 916 |     EventPipeBuffer *pIter = m_pHeadBuffer; | 
| 917 |     while(pIter->GetNext() != NULL) | 
| 918 |     { | 
| 919 |         pIter = pIter->GetNext(); | 
| 920 |         nodeCount++; | 
| 921 |  | 
| 922 |         // Check for consistency of the buffer itself. | 
| 923 |         // NOTE: We can't check the last buffer because the owning thread could | 
| 924 |         // be writing to it, which could result in false asserts. | 
| 925 |         if(pIter->GetNext() != NULL) | 
| 926 |         { | 
| 927 |             _ASSERTE(pIter->EnsureConsistency()); | 
| 928 |         } | 
| 929 |  | 
| 930 |         // Check for cycles. | 
| 931 |         _ASSERTE(nodeCount <= m_bufferCount); | 
| 932 |     } | 
| 933 |  | 
| 934 |     // When we're done with the walk, pIter must point to the tail node. | 
| 935 |     _ASSERTE(pIter == m_pTailBuffer); | 
| 936 |  | 
| 937 |     // Node count must equal the buffer count. | 
| 938 |     _ASSERTE(nodeCount == m_bufferCount); | 
| 939 |  | 
| 940 |     // Now, walk the list in reverse. | 
| 941 |     pIter = m_pTailBuffer; | 
| 942 |     nodeCount = (m_pTailBuffer != NULL) ? 1 : 0; | 
| 943 |     while(pIter->GetPrevious() != NULL) | 
| 944 |     { | 
| 945 |         pIter = pIter->GetPrevious(); | 
| 946 |         nodeCount++; | 
| 947 |  | 
| 948 |         // Check for cycles. | 
| 949 |         _ASSERTE(nodeCount <= m_bufferCount); | 
| 950 |     } | 
| 951 |  | 
| 952 |     // When we're done with the reverse walk, pIter must point to the head node. | 
| 953 |     _ASSERTE(pIter == m_pHeadBuffer); | 
| 954 |  | 
| 955 |     // Node count must equal the buffer count. | 
| 956 |     _ASSERTE(nodeCount == m_bufferCount); | 
| 957 |  | 
| 958 |     // We're done. | 
| 959 |     return true; | 
| 960 | } | 
| 961 | #endif // _DEBUG | 
| 962 |  | 
| 963 | #endif // FEATURE_PERFTRACING | 
| 964 |  |