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
41 extern 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.
48 class BucketManager
49 {
50 public:
51 
52     uint32_t mCurrentFrame;
53     std::vector<uint32_t> mBucketMap;
54     bool                  mBucketsInitialized;
55     std::string           mBucketMgrName;
56 
57 
BucketManager(std::string name)58     BucketManager(std::string name) : mCurrentFrame(0), mBucketsInitialized(false), mBucketMgrName(name)
59     {
60         mBucketMap.clear();
61     }
62     ~BucketManager();
63 
64     // removes all registered thread data
ClearThreads()65     void ClearThreads()
66     {
67         mThreadMutex.lock();
68         mThreads.clear();
69         mThreadMutex.unlock();
70     }
71 
72     // removes all registered buckets
ClearBuckets()73     void ClearBuckets()
74     {
75         mThreadMutex.lock();
76         mBuckets.clear();
77         mThreadMutex.unlock();
78     }
79 
80     /// Registers a new thread with the manager.
81     /// @param name - name of thread, used for labels in reports and threadviz
82     void RegisterThread(const std::string& name);
83 
84     /// Registers a new bucket type with the manager.  Returns a unique
85     /// id which should be used in subsequent calls to start/stop the bucket
86     /// @param desc - description of the bucket
87     /// @return unique id
88     UINT RegisterBucket(const BUCKET_DESC& desc);
89 
90     // print report
91     void PrintReport(const std::string& filename);
92 
93 
94     // start capturing
95     void StartCapture();
96 
97     // stop capturing
StopCapture()98     INLINE void StopCapture()
99     {
100         mCapturing = false;
101 
102         // wait for all threads to pop back to root bucket
103         bool stillCapturing = true;
104         while (stillCapturing)
105         {
106             stillCapturing = false;
107             for (const BUCKET_THREAD& t : mThreads)
108             {
109                 if (t.level > 0)
110                 {
111                     stillCapturing = true;
112                     continue;
113                 }
114             }
115         }
116 
117         mDoneCapturing = true;
118         printf("Capture Stopped\n");
119     }
120 
121     // start a bucket
122     // @param id generated by RegisterBucket
StartBucket(UINT id)123     INLINE void StartBucket(UINT id)
124     {
125         if (!mCapturing)
126             return;
127 
128         SWR_ASSERT(tlsThreadId < mThreads.size());
129 
130         BUCKET_THREAD& bt = mThreads[tlsThreadId];
131 
132         uint64_t tsc = __rdtsc();
133 
134         {
135             if (bt.pCurrent->children.size() < mBuckets.size())
136             {
137                 bt.pCurrent->children.resize(mBuckets.size());
138             }
139             BUCKET& child = bt.pCurrent->children[id];
140             child.pParent = bt.pCurrent;
141             child.id      = id;
142             child.start   = tsc;
143 
144             // update thread's currently executing bucket
145             bt.pCurrent = &child;
146         }
147 
148 
149         bt.level++;
150     }
151 
152     // stop the currently executing bucket
StopBucket(UINT id)153     INLINE void StopBucket(UINT id)
154     {
155         SWR_ASSERT(tlsThreadId < mThreads.size());
156         BUCKET_THREAD& bt = mThreads[tlsThreadId];
157 
158         if (bt.level == 0)
159         {
160             return;
161         }
162 
163         uint64_t tsc = __rdtsc();
164 
165         {
166             if (bt.pCurrent->start == 0)
167                 return;
168             SWR_ASSERT(bt.pCurrent->id == id, "Mismatched buckets detected");
169 
170             bt.pCurrent->elapsed += (tsc - bt.pCurrent->start);
171             bt.pCurrent->count++;
172 
173             // pop to parent
174             bt.pCurrent = bt.pCurrent->pParent;
175         }
176 
177         bt.level--;
178     }
179 
AddEvent(uint32_t id,uint32_t count)180     INLINE void AddEvent(uint32_t id, uint32_t count)
181     {
182         if (!mCapturing)
183             return;
184 
185         SWR_ASSERT(tlsThreadId < mThreads.size());
186 
187         BUCKET_THREAD& bt = mThreads[tlsThreadId];
188 
189         // don't record events for threadviz
190         {
191             if (bt.pCurrent->children.size() < mBuckets.size())
192             {
193                 bt.pCurrent->children.resize(mBuckets.size());
194             }
195             BUCKET& child = bt.pCurrent->children[id];
196             child.pParent = bt.pCurrent;
197             child.id      = id;
198             child.count += count;
199         }
200     }
201 
202 private:
203     void PrintBucket(
204         FILE* f, UINT level, uint64_t threadCycles, uint64_t parentCycles, const BUCKET& bucket);
205     void PrintThread(FILE* f, const BUCKET_THREAD& thread);
206 
207     // list of active threads that have registered with this manager
208     std::vector<BUCKET_THREAD> mThreads;
209 
210     // list of buckets registered with this manager
211     std::vector<BUCKET_DESC> mBuckets;
212 
213     // is capturing currently enabled
214     volatile bool mCapturing{false};
215 
216     // has capturing completed
217     volatile bool mDoneCapturing{false};
218 
219     std::mutex mThreadMutex;
220 
221     std::string mThreadVizDir;
222 
223 };
224 
225 // C helpers for jitter
226 void BucketManager_StartBucket(BucketManager* pBucketMgr, uint32_t id);
227 void BucketManager_StopBucket(BucketManager* pBucketMgr, uint32_t id);
228