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.cpp
24 *
25 * @brief implementation of rdtsc buckets.
26 *
27 * Notes:
28 *
29 ******************************************************************************/
30#include "rdtsc_buckets.h"
31#include <inttypes.h>
32
33#if defined(_WIN32)
34#define PATH_SEPARATOR "\\"
35#elif defined(__unix__) || defined(__APPLE__)
36#define PATH_SEPARATOR "/"
37#else
38#error "Unsupported platform"
39#endif
40
41THREAD UINT tlsThreadId = 0;
42
43BucketManager::~BucketManager()
44{
45}
46
47void BucketManager::RegisterThread(const std::string& name)
48{
49
50    BUCKET_THREAD newThread;
51    newThread.name = name;
52    newThread.root.children.reserve(mBuckets.size());
53    newThread.root.id      = 0;
54    newThread.root.pParent = nullptr;
55    newThread.pCurrent     = &newThread.root;
56
57    mThreadMutex.lock();
58
59    // assign unique thread id for this thread
60    size_t id    = mThreads.size();
61    newThread.id = (UINT)id;
62    tlsThreadId  = (UINT)id;
63
64    // store new thread
65    mThreads.push_back(newThread);
66
67    mThreadMutex.unlock();
68}
69
70UINT BucketManager::RegisterBucket(const BUCKET_DESC& desc)
71{
72    mThreadMutex.lock();
73    size_t id = mBuckets.size();
74    mBuckets.push_back(desc);
75    mThreadMutex.unlock();
76    return (UINT)id;
77}
78
79void BucketManager::PrintBucket(
80    FILE* f, UINT level, uint64_t threadCycles, uint64_t parentCycles, const BUCKET& bucket)
81{
82    const char* arrows[] = {
83        "",
84        "|-> ",
85        "    |-> ",
86        "        |-> ",
87        "            |-> ",
88        "                |-> ",
89        "                    |-> ",
90        "                        |-> ",
91        "                            |-> ",
92    };
93
94    // compute percent of total cycles used by this bucket
95    float percentTotal = (float)((double)bucket.elapsed / (double)threadCycles * 100.0);
96
97    // compute percent of parent cycles used by this bucket
98    float percentParent = (float)((double)bucket.elapsed / (double)parentCycles * 100.0);
99
100    // compute average cycle count per invocation
101    uint64_t CPE = bucket.elapsed / bucket.count;
102
103    BUCKET_DESC& desc = mBuckets[bucket.id];
104
105    // construct hierarchy visualization
106    std::string str = arrows[level];
107    str += desc.name;
108    char hier[80];
109    strcpy_s(hier, sizeof(hier)-1, str.c_str());
110
111    // print out
112    fprintf(f,
113            "%6.2f %6.2f %-10" PRIu64 " %-10" PRIu64 " %-10u %-10lu %-10u %s\n",
114            percentTotal,
115            percentParent,
116            bucket.elapsed,
117            CPE,
118            bucket.count,
119            (unsigned long)0,
120            (uint32_t)0,
121            hier);
122
123    // dump all children of this bucket
124    for (const BUCKET& child : bucket.children)
125    {
126        if (child.count)
127        {
128            PrintBucket(f, level + 1, threadCycles, bucket.elapsed, child);
129        }
130    }
131}
132
133void BucketManager::PrintThread(FILE* f, const BUCKET_THREAD& thread)
134{
135    // print header
136    fprintf(f, "\nThread %u (%s)\n", thread.id, thread.name.c_str());
137    fprintf(f, " %%Tot   %%Par  Cycles     CPE        NumEvent   CPE2       NumEvent2  Bucket\n");
138
139    // compute thread level total cycle counts across all buckets from root
140    const BUCKET& root        = thread.root;
141    uint64_t      totalCycles = 0;
142    for (const BUCKET& child : root.children)
143    {
144        totalCycles += child.elapsed;
145    }
146
147    for (const BUCKET& child : root.children)
148    {
149        if (child.count)
150        {
151            PrintBucket(f, 0, totalCycles, totalCycles, child);
152        }
153    }
154}
155
156void BucketManager::PrintReport(const std::string& filename)
157{
158    {
159        FILE* f = fopen(filename.c_str(), "w");
160        assert(f);
161
162        mThreadMutex.lock();
163        for (const BUCKET_THREAD& thread : mThreads)
164        {
165            PrintThread(f, thread);
166            fprintf(f, "\n");
167        }
168
169        mThreadMutex.unlock();
170
171        fclose(f);
172    }
173}
174
175
176void BucketManager::StartCapture()
177{
178
179    printf("Capture Starting\n");
180
181    mCapturing = true;
182}
183
184void BucketManager_StartBucket(BucketManager* pBucketMgr, uint32_t id)
185{
186    pBucketMgr->StartBucket(id);
187}
188
189void BucketManager_StopBucket(BucketManager* pBucketMgr, uint32_t id)
190{
191    pBucketMgr->StopBucket(id);
192}
193