blob: 54820b14457fe8a10bc9e53435cd2e193eff8c7c [file] [log] [blame]
Yabin Cuiecb9a302015-07-01 10:00:52 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef SIMPLE_PERF_CALLCHAIN_H_
18#define SIMPLE_PERF_CALLCHAIN_H_
19
Yabin Cuib64a8632016-05-24 18:23:33 -070020#include <string.h>
21
22#include <algorithm>
Yabin Cuiecb9a302015-07-01 10:00:52 -070023#include <memory>
Yabin Cuib64a8632016-05-24 18:23:33 -070024#include <queue>
Yabin Cuiecb9a302015-07-01 10:00:52 -070025#include <vector>
26
Yabin Cuib64a8632016-05-24 18:23:33 -070027#include <android-base/logging.h>
Yabin Cuiecb9a302015-07-01 10:00:52 -070028
Yabin Cuib64a8632016-05-24 18:23:33 -070029template <typename EntryT>
Yabin Cuiecb9a302015-07-01 10:00:52 -070030struct CallChainNode {
31 uint64_t period;
32 uint64_t children_period;
Yabin Cuib64a8632016-05-24 18:23:33 -070033 std::vector<EntryT*> chain;
Yabin Cuiecb9a302015-07-01 10:00:52 -070034 std::vector<std::unique_ptr<CallChainNode>> children;
35};
36
Yabin Cuib64a8632016-05-24 18:23:33 -070037template <typename EntryT>
Yabin Cuiecb9a302015-07-01 10:00:52 -070038struct CallChainRoot {
Yabin Cuib64a8632016-05-24 18:23:33 -070039 typedef CallChainNode<EntryT> NodeT;
Yabin Cuiecb9a302015-07-01 10:00:52 -070040 uint64_t children_period;
Yabin Cuib64a8632016-05-24 18:23:33 -070041 std::vector<std::unique_ptr<NodeT>> children;
Yabin Cuiecb9a302015-07-01 10:00:52 -070042
Yabin Cuib64a8632016-05-24 18:23:33 -070043 CallChainRoot() : children_period(0) {}
44
45 void AddCallChain(const std::vector<EntryT*>& callchain, uint64_t period) {
46 children_period += period;
47 NodeT* p = FindMatchingNode(children, callchain[0]);
48 if (p == nullptr) {
49 std::unique_ptr<NodeT> new_node = AllocateNode(callchain, 0, period, 0);
50 children.push_back(std::move(new_node));
51 return;
52 }
53 size_t callchain_pos = 0;
54 while (true) {
55 size_t match_length =
56 GetMatchingLengthInNode(p, callchain, callchain_pos);
57 CHECK_GT(match_length, 0u);
58 callchain_pos += match_length;
59 bool find_child = true;
60 if (match_length < p->chain.size()) {
61 SplitNode(p, match_length);
62 find_child = false; // No need to find matching node in p->children.
63 }
64 if (callchain_pos == callchain.size()) {
65 p->period += period;
66 return;
67 }
68 p->children_period += period;
69 if (find_child) {
70 NodeT* np = FindMatchingNode(p->children, callchain[callchain_pos]);
71 if (np != nullptr) {
72 p = np;
73 continue;
74 }
75 }
76 std::unique_ptr<NodeT> new_node =
77 AllocateNode(callchain, callchain_pos, period, 0);
78 p->children.push_back(std::move(new_node));
79 break;
80 }
Yabin Cuiecb9a302015-07-01 10:00:52 -070081 }
82
Yabin Cuib64a8632016-05-24 18:23:33 -070083 void SortByPeriod() {
84 std::queue<std::vector<std::unique_ptr<NodeT>>*> queue;
85 queue.push(&children);
86 while (!queue.empty()) {
87 std::vector<std::unique_ptr<NodeT>>* v = queue.front();
88 queue.pop();
89 std::sort(v->begin(), v->end(), CallChainRoot::CompareNodeByPeriod);
90 for (auto& node : *v) {
91 if (!node->children.empty()) {
92 queue.push(&node->children);
93 }
94 }
95 }
96 }
97
98 private:
99 NodeT* FindMatchingNode(const std::vector<std::unique_ptr<NodeT>>& nodes,
100 const EntryT* sample) {
101 for (auto& node : nodes) {
102 if (MatchSampleByName(node->chain.front(), sample)) {
103 return node.get();
104 }
105 }
106 return nullptr;
107 }
108
109 size_t GetMatchingLengthInNode(NodeT* node, const std::vector<EntryT*>& chain,
110 size_t chain_start) {
111 size_t i, j;
112 for (i = 0, j = chain_start; i < node->chain.size() && j < chain.size();
113 ++i, ++j) {
114 if (!MatchSampleByName(node->chain[i], chain[j])) {
115 break;
116 }
117 }
118 return i;
119 }
120
121 bool MatchSampleByName(const EntryT* sample1, const EntryT* sample2) {
122 return strcmp(sample1->symbol->Name(), sample2->symbol->Name()) == 0;
123 }
124
125 void SplitNode(NodeT* parent, size_t parent_length) {
126 std::unique_ptr<NodeT> child = AllocateNode(
127 parent->chain, parent_length, parent->period, parent->children_period);
128 child->children = std::move(parent->children);
129 parent->period = 0;
130 parent->children_period = child->period + child->children_period;
131 parent->chain.resize(parent_length);
132 parent->children.clear();
133 parent->children.push_back(std::move(child));
134 }
135
136 std::unique_ptr<NodeT> AllocateNode(const std::vector<EntryT*>& chain,
137 size_t chain_start, uint64_t period,
138 uint64_t children_period) {
139 std::unique_ptr<NodeT> node(new NodeT);
140 for (size_t i = chain_start; i < chain.size(); ++i) {
141 node->chain.push_back(chain[i]);
142 }
143 node->period = period;
144 node->children_period = children_period;
145 return node;
146 }
147
148 static bool CompareNodeByPeriod(const std::unique_ptr<NodeT>& n1,
149 const std::unique_ptr<NodeT>& n2) {
150 uint64_t period1 = n1->period + n1->children_period;
151 uint64_t period2 = n2->period + n2->children_period;
152 return period1 > period2;
153 }
Yabin Cuiecb9a302015-07-01 10:00:52 -0700154};
155
156#endif // SIMPLE_PERF_CALLCHAIN_H_