1/****************************************************************************
2 * Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * @file rdtsc_buckets.h
24 *
25 * @brief declaration for rdtsc buckets.
26 *
27 * Notes:
28 *
29 ******************************************************************************/
30#pragma once
31
32#include "os.h"
33#include <vector>
34#include <mutex>
35#include <sstream>
36
37#include "rdtsc_buckets_shared.h"
38
39
40// unique thread id stored in thread local storage
41extern THREAD UINT tlsThreadId;
42
43//////////////////////////////////////////////////////////////////////////
44/// @brief BucketManager encapsulates a single instance of the buckets
45///        functionality. There can be one or many bucket managers active
46///        at any time.  The manager owns all the threads and
47///        bucket information that have been registered to it.
48class BucketManager
49{
50public:
51    BucketManager() {}
52    ~BucketManager();
53
54    // removes all registered thread data
55    void ClearThreads()
56    {
57        mThreadMutex.lock();
58        mThreads.clear();
59        mThreadMutex.unlock();
60    }
61
62    // removes all registered buckets
63    void ClearBuckets()
64    {
65        mThreadMutex.lock();
66        mBuckets.clear();
67        mThreadMutex.unlock();
68    }
69
70    /// Registers a new thread with the manager.
71    /// @param name - name of thread, used for labels in reports and threadviz
72    void RegisterThread(const std::string& name);
73
74    /// Registers a new bucket type with the manager.  Returns a unique
75    /// id which should be used in subsequent calls to start/stop the bucket
76    /// @param desc - description of the bucket
77    /// @return unique id
78    UINT RegisterBucket(const BUCKET_DESC& desc);
79
80    // print report
81    void PrintReport(const std::string& filename);
82
83
84    // start capturing
85    void StartCapture();
86
87    // stop capturing
88    INLINE void StopCapture()
89    {
90        mCapturing = false;
91
92        // wait for all threads to pop back to root bucket
93        bool stillCapturing = true;
94        while (stillCapturing)
95        {
96            stillCapturing = false;
97            for (const BUCKET_THREAD& t : mThreads)
98            {
99                if (t.level > 0)
100                {
101                    stillCapturing = true;
102                    continue;
103                }
104            }
105        }
106
107        mDoneCapturing = true;
108        printf("Capture Stopped\n");
109    }
110
111    // start a bucket
112    // @param id generated by RegisterBucket
113    INLINE void StartBucket(UINT id)
114    {
115        if (!mCapturing)
116            return;
117
118        SWR_ASSERT(tlsThreadId < mThreads.size());
119
120        BUCKET_THREAD& bt = mThreads[tlsThreadId];
121
122        uint64_t tsc = __rdtsc();
123
124        {
125            if (bt.pCurrent->children.size() < mBuckets.size())
126            {
127                bt.pCurrent->children.resize(mBuckets.size());
128            }
129            BUCKET& child = bt.pCurrent->children[id];
130            child.pParent = bt.pCurrent;
131            child.id      = id;
132            child.start   = tsc;
133
134            // update thread's currently executing bucket
135            bt.pCurrent = &child;
136        }
137
138
139        bt.level++;
140    }
141
142    // stop the currently executing bucket
143    INLINE void StopBucket(UINT id)
144    {
145        SWR_ASSERT(tlsThreadId < mThreads.size());
146        BUCKET_THREAD& bt = mThreads[tlsThreadId];
147
148        if (bt.level == 0)
149        {
150            return;
151        }
152
153        uint64_t tsc = __rdtsc();
154
155        {
156            if (bt.pCurrent->start == 0)
157                return;
158            SWR_ASSERT(bt.pCurrent->id == id, "Mismatched buckets detected");
159
160            bt.pCurrent->elapsed += (tsc - bt.pCurrent->start);
161            bt.pCurrent->count++;
162
163            // pop to parent
164            bt.pCurrent = bt.pCurrent->pParent;
165        }
166
167        bt.level--;
168    }
169
170    INLINE void AddEvent(uint32_t id, uint32_t count)
171    {
172        if (!mCapturing)
173            return;
174
175        SWR_ASSERT(tlsThreadId < mThreads.size());
176
177        BUCKET_THREAD& bt = mThreads[tlsThreadId];
178
179        // don't record events for threadviz
180        {
181            if (bt.pCurrent->children.size() < mBuckets.size())
182            {
183                bt.pCurrent->children.resize(mBuckets.size());
184            }
185            BUCKET& child = bt.pCurrent->children[id];
186            child.pParent = bt.pCurrent;
187            child.id      = id;
188            child.count += count;
189        }
190    }
191
192private:
193    void PrintBucket(
194        FILE* f, UINT level, uint64_t threadCycles, uint64_t parentCycles, const BUCKET& bucket);
195    void PrintThread(FILE* f, const BUCKET_THREAD& thread);
196
197    // list of active threads that have registered with this manager
198    std::vector<BUCKET_THREAD> mThreads;
199
200    // list of buckets registered with this manager
201    std::vector<BUCKET_DESC> mBuckets;
202
203    // is capturing currently enabled
204    volatile bool mCapturing{false};
205
206    // has capturing completed
207    volatile bool mDoneCapturing{false};
208
209    std::mutex mThreadMutex;
210
211    std::string mThreadVizDir;
212
213};
214
215// C helpers for jitter
216void BucketManager_StartBucket(BucketManager* pBucketMgr, uint32_t id);
217void BucketManager_StopBucket(BucketManager* pBucketMgr, uint32_t id);
218