2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/tools/tc-print/perf-events.h"
18 #include <unordered_map>
20 #include "hphp/tools/tc-print/tc-print.h"
22 namespace HPHP
{ namespace jit
{
28 PerfEventType
perfScriptOutputToEventType(const char* eventId
) {
30 // Events are duplicated so we can use the non-generic perf events.
31 std::pair
<const char*, PerfEventType
> idToType
[] = {
32 std::make_pair("cycles", EVENT_CYCLES
),
33 std::make_pair("cpu-clock", EVENT_CYCLES
),
35 std::make_pair("branch-misses", EVENT_BRANCH_MISSES
),
36 std::make_pair("0x5300c5", EVENT_BRANCH_MISSES
),
38 // fake event grouping all icache misses
39 std::make_pair("L1-icache-misses", EVENT_ICACHE_MISSES
),
40 // real icache miss events
41 std::make_pair("L1-icache-load-misses", EVENT_ICACHE_MISSES
),
42 std::make_pair("L1-icache-prefetch-misses", EVENT_ICACHE_MISSES
),
44 // fake event grouping all dcache misses
45 std::make_pair("L1-dcache-misses", EVENT_DCACHE_MISSES
),
46 // real dcache miss events
47 std::make_pair("L1-dcache-load-misses", EVENT_DCACHE_MISSES
),
48 std::make_pair("L1-dcache-store-misses", EVENT_DCACHE_MISSES
),
49 std::make_pair("L1-dcache-prefetch-misses", EVENT_DCACHE_MISSES
),
51 std::make_pair("cache-misses", EVENT_LLC_MISSES
),
52 std::make_pair("LLC-store-misses", EVENT_LLC_STORE_MISSES
),
54 // fake event grouping all iTLB misses
55 std::make_pair("iTLB-misses", EVENT_ITLB_MISSES
),
56 // real iTLB miss events
57 std::make_pair("iTLB-load-misses", EVENT_ITLB_MISSES
),
59 // fake event grouping all dTLB misses
60 std::make_pair("dTLB-misses", EVENT_DTLB_MISSES
),
61 // real dTLB miss events
62 std::make_pair("dTLB-load-misses", EVENT_DTLB_MISSES
),
63 std::make_pair("dTLB-store-misses", EVENT_DTLB_MISSES
),
64 std::make_pair("dTLB-prefetch-misses", EVENT_DTLB_MISSES
),
67 size_t numEle
= sizeof idToType
/ sizeof (*idToType
);
68 for (size_t i
= 0; i
< numEle
; i
++) {
69 if (!strcmp(idToType
[i
].first
, eventId
)) return idToType
[i
].second
;
72 return NUM_EVENT_TYPES
;
75 const char* validArguments
[] = {
86 PerfEventType
commandLineArgumentToEventType(const char* argument
) {
87 constexpr size_t numEle
= sizeof validArguments
/ sizeof (*validArguments
);
88 static_assert(numEle
== NUM_EVENT_TYPES
, "need to update validArguments[]");
90 for (size_t i
= 0; i
< numEle
; i
++) {
91 if (!strcmp(validArguments
[i
], argument
)) return (PerfEventType
)i
;
94 return NUM_EVENT_TYPES
;
97 const char* eventTypeToCommandLineArgument(PerfEventType eventType
) {
98 always_assert(eventType
!= NUM_EVENT_TYPES
);
99 return validArguments
[eventType
];
104 struct StackTraceTree::Node
{
105 typedef std::unordered_map
<string
, Node
*> StrToPNode
;
107 uint64_t inclusiveCount
;
108 uint64_t selfCount
; // samples that happen precisely at this node
111 Node() : inclusiveCount(0), selfCount(0) {};
114 Node::StrToPNode::iterator it
;
116 for (it
= children
.begin(); it
!= children
.end(); it
++) {
122 // These are private so that we don't accidentally copy and later
123 // double-delete one of of the pointers in children.
124 Node(const Node
& other
);
125 Node
& operator=(Node other
);
129 StackTraceTree::StackTraceTree() : root(new Node()) {}
131 void StackTraceTree::insert(const vector
<string
>& trace
, uint64_t count
) {
132 if (trace
.empty()) return;
133 root
.get()->inclusiveCount
+= count
;
135 Node
* curr
= root
.get();
136 for (size_t i
= 0; i
< trace
.size(); i
++) {
137 Node::StrToPNode::const_iterator it
= curr
->children
.find(trace
[i
]);
139 if (it
== curr
->children
.end()) {
140 curr
->children
.insert(std::pair
<string
, Node
*>(trace
[i
], new Node()));
143 curr
= curr
->children
[trace
[i
]];
144 curr
->inclusiveCount
+= count
;
147 curr
->selfCount
+= count
;
150 void StackTraceTree::printTop(double minPercentage
,
151 uint64_t eventCount
) const {
153 always_assert(eventCount
);
154 uint64_t helpersCount
= root
.get()->inclusiveCount
;
156 printf("+ samples in helpers: %" PRIu64
" (%5.2lf%%)\n",
158 helpersCount
* 100.0 / eventCount
);
160 vector
<uint32_t> activeColumns
;
161 printNode(root
.get(), minPercentage
, 1, activeColumns
, eventCount
);
164 void printSeparators(uint32_t n
, vector
<uint32_t>& activeColumns
) {
166 always_assert(!activeColumns
.empty());
169 for (uint32_t i
= 1; i
<= n
; i
++) {
171 if (cc
< activeColumns
.size() && activeColumns
[cc
] == i
) {
179 void printGuides(uint32_t depth
, vector
<uint32_t>& activeColumns
) {
180 printSeparators(depth
, activeColumns
);
182 always_assert(depth
);
183 printSeparators(depth
- 1, activeColumns
);
187 void StackTraceTree::printNode(const Node
* node
,
188 double minPercentage
,
190 vector
<uint32_t>& activeColumns
,
191 uint64_t eventCount
) const {
192 always_assert(eventCount
);
193 std::vector
<std::pair
<uint64_t, string
> > activeChildren
;
195 Node::StrToPNode::const_iterator it
;
196 for (it
= node
->children
.begin(); it
!= node
->children
.end(); it
++) {
197 if (it
->second
->inclusiveCount
* 100.0 >= minPercentage
* eventCount
) {
198 activeChildren
.push_back(make_pair(it
->second
->inclusiveCount
,
203 std::sort(activeChildren
.rbegin(), activeChildren
.rend());
204 if (!activeChildren
.empty()) activeColumns
.push_back(depth
);
206 for (size_t i
= 0; i
< activeChildren
.size(); i
++) {
208 it
= node
->children
.find(activeChildren
[i
].second
);
209 always_assert(it
!= node
->children
.end());
210 const Node
* next
= it
->second
;
212 printGuides(depth
, activeColumns
);
213 if (i
+ 1 == activeChildren
.size()) activeColumns
.pop_back();
215 printf("%" PRIu64
" (%.2lf%%) self: %" PRIu64
" (%.2lf%%) %s\n",
216 next
->inclusiveCount
,
217 next
->inclusiveCount
* 100.0 / eventCount
,
219 next
->selfCount
* 100.0 / eventCount
,
220 activeChildren
[i
].second
.c_str());
230 void StackTraceTree::aggregate(const Node
* from
, Node
*to
) {
231 always_assert(from
!= nullptr && to
!= nullptr);
233 to
->inclusiveCount
+= from
->inclusiveCount
;
234 to
->selfCount
+= from
->selfCount
;
236 Node::StrToPNode::const_iterator itFrom
, itTo
;
238 for (itFrom
= from
->children
.begin();
239 itFrom
!= from
->children
.end();
242 itTo
= to
->children
.find(itFrom
->first
);
243 if (itTo
== to
->children
.end()) {
244 to
->children
.insert({itFrom
->first
, new Node()});
247 aggregate(itFrom
->second
, to
->children
[itFrom
->first
]);
251 void StackTraceTree::aggregateTree(const StackTraceTree
& other
) {
252 aggregate(other
.root
.get(), root
.get());