Only break Profile translations on existing SrcRecs
[hiphop-php.git] / hphp / tools / tc-print / perf-events.cpp
blob0c05d4b458ca6663f2df3f5f9231d3258cd6b4b6
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 {
24 using std::string;
26 // PerfEvent
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[] = {
76 "cycles",
77 "branch-misses",
78 "L1-icache-misses",
79 "L1-dcache-misses",
80 "cache-misses",
81 "LLC-store-misses",
82 "iTLB-misses",
83 "dTLB-misses",
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];
102 // StackTraceTree
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
109 StrToPNode children;
111 Node() : inclusiveCount(0), selfCount(0) {};
113 ~Node() {
114 Node::StrToPNode::iterator it;
116 for (it = children.begin(); it != children.end(); it++) {
117 delete(it->second);
121 private:
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",
157 helpersCount,
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) {
165 if (!n) return;
166 always_assert(!activeColumns.empty());
168 uint32_t cc = 0;
169 for (uint32_t i = 1; i <= n; i++) {
170 char c = ' ';
171 if (cc < activeColumns.size() && activeColumns[cc] == i) {
172 c = '|';
173 cc++;
175 printf("%c ", c);
179 void printGuides(uint32_t depth, vector<uint32_t>& activeColumns) {
180 printSeparators(depth, activeColumns);
181 printf("\n");
182 always_assert(depth);
183 printSeparators(depth - 1, activeColumns);
184 printf("+--+ ");
187 void StackTraceTree::printNode(const Node* node,
188 double minPercentage,
189 uint32_t depth,
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,
199 it->first));
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,
218 next->selfCount,
219 next->selfCount * 100.0 / eventCount,
220 activeChildren[i].second.c_str());
222 printNode(next,
223 minPercentage,
224 depth + 1,
225 activeColumns,
226 eventCount);
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();
240 itFrom++) {
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());