/****************************************************************************
* 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 <vector>
#include <mutex>
#include <sstream>
#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<BUCKET_THREAD> mThreads;
// list of buckets registered with this manager
std::vector<BUCKET_DESC> 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);