/**************************************************************************** * Copyright (C) 2014-2015 Intel Corporation. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * @file rdtsc_buckets.h * * @brief declaration for rdtsc buckets. * * Notes: * ******************************************************************************/ #pragma once #include "os.h" #include #include #include #include "rdtsc_buckets_shared.h" // unique thread id stored in thread local storage extern THREAD UINT tlsThreadId; ////////////////////////////////////////////////////////////////////////// /// @brief BucketManager encapsulates a single instance of the buckets /// functionality. There can be one or many bucket managers active /// at any time. The manager owns all the threads and /// bucket information that have been registered to it. class BucketManager { public: BucketManager() {} ~BucketManager(); // removes all registered thread data void ClearThreads() { mThreadMutex.lock(); mThreads.clear(); mThreadMutex.unlock(); } // removes all registered buckets void ClearBuckets() { mThreadMutex.lock(); mBuckets.clear(); mThreadMutex.unlock(); } /// Registers a new thread with the manager. /// @param name - name of thread, used for labels in reports and threadviz void RegisterThread(const std::string& name); /// Registers a new bucket type with the manager. Returns a unique /// id which should be used in subsequent calls to start/stop the bucket /// @param desc - description of the bucket /// @return unique id UINT RegisterBucket(const BUCKET_DESC& desc); // print report void PrintReport(const std::string& filename); // start capturing void StartCapture(); // stop capturing INLINE void StopCapture() { mCapturing = false; // wait for all threads to pop back to root bucket bool stillCapturing = true; while (stillCapturing) { stillCapturing = false; for (const BUCKET_THREAD& t : mThreads) { if (t.level > 0) { stillCapturing = true; continue; } } } mDoneCapturing = true; printf("Capture Stopped\n"); } // start a bucket // @param id generated by RegisterBucket INLINE void StartBucket(UINT id) { if (!mCapturing) return; SWR_ASSERT(tlsThreadId < mThreads.size()); BUCKET_THREAD& bt = mThreads[tlsThreadId]; uint64_t tsc = __rdtsc(); { if (bt.pCurrent->children.size() < mBuckets.size()) { bt.pCurrent->children.resize(mBuckets.size()); } BUCKET& child = bt.pCurrent->children[id]; child.pParent = bt.pCurrent; child.id = id; child.start = tsc; // update thread's currently executing bucket bt.pCurrent = &child; } bt.level++; } // stop the currently executing bucket INLINE void StopBucket(UINT id) { SWR_ASSERT(tlsThreadId < mThreads.size()); BUCKET_THREAD& bt = mThreads[tlsThreadId]; if (bt.level == 0) { return; } uint64_t tsc = __rdtsc(); { if (bt.pCurrent->start == 0) return; SWR_ASSERT(bt.pCurrent->id == id, "Mismatched buckets detected"); bt.pCurrent->elapsed += (tsc - bt.pCurrent->start); bt.pCurrent->count++; // pop to parent bt.pCurrent = bt.pCurrent->pParent; } bt.level--; } INLINE void AddEvent(uint32_t id, uint32_t count) { if (!mCapturing) return; SWR_ASSERT(tlsThreadId < mThreads.size()); BUCKET_THREAD& bt = mThreads[tlsThreadId]; // don't record events for threadviz { if (bt.pCurrent->children.size() < mBuckets.size()) { bt.pCurrent->children.resize(mBuckets.size()); } BUCKET& child = bt.pCurrent->children[id]; child.pParent = bt.pCurrent; child.id = id; child.count += count; } } private: void PrintBucket( FILE* f, UINT level, uint64_t threadCycles, uint64_t parentCycles, const BUCKET& bucket); void PrintThread(FILE* f, const BUCKET_THREAD& thread); // list of active threads that have registered with this manager std::vector mThreads; // list of buckets registered with this manager std::vector mBuckets; // is capturing currently enabled volatile bool mCapturing{false}; // has capturing completed volatile bool mDoneCapturing{false}; std::mutex mThreadMutex; std::string mThreadVizDir; }; // C helpers for jitter void BucketManager_StartBucket(BucketManager* pBucketMgr, uint32_t id); void BucketManager_StopBucket(BucketManager* pBucketMgr, uint32_t id);