1 #include "tree_merger.hh"
2 #include "../execution.hh"
3 #include "../tree/structure.hh"
4 #include "../core.hh"
5
6 #include "../utils/utils.hh"
7 #include "../utils/tree_utils.hh"
8
9 #include <QStack>
10
11 namespace cpprofiler
12 {
13 namespace analysis
14 {
15
16 using namespace tree;
17
TreeMerger(const Execution & ex_l_,const Execution & ex_r_,std::shared_ptr<tree::NodeTree> tree,std::shared_ptr<analysis::MergeResult> res,std::shared_ptr<std::vector<OriginalLoc>> orig_locs)18 TreeMerger::TreeMerger(const Execution &ex_l_,
19 const Execution &ex_r_,
20 std::shared_ptr<tree::NodeTree> tree,
21 std::shared_ptr<analysis::MergeResult> res,
22 std::shared_ptr<std::vector<OriginalLoc>> orig_locs)
23 : ex_l(ex_l_), ex_r(ex_r_),
24 tree_l(ex_l.tree()),
25 tree_r(ex_r.tree()),
26 res_tree(tree),
27 merge_result(res),
28 orig_locs_(orig_locs)
29 {
30
31 connect(this, &QThread::finished, this, &QObject::deleteLater);
32 }
33
~TreeMerger()34 TreeMerger::~TreeMerger()
35 {
36 }
37
find_and_replace_all(std::string & str,std::string substr_old,std::string substr_new)38 static void find_and_replace_all(std::string &str, std::string substr_old, std::string substr_new)
39 {
40 auto pos = str.find(substr_old);
41 while (pos != std::string::npos)
42 {
43 str.replace(pos, std::string(substr_old).length(), substr_new);
44 pos = str.find(substr_old);
45 }
46 }
47
labelsEqual(std::string lhs,std::string rhs)48 static bool labelsEqual(std::string lhs, std::string rhs)
49 {
50 /// NOTE(maxim): removes whitespaces before comparing;
51 /// this will be necessary as long as Chuffed and Gecode don't agree
52 /// on whether to put whitespaces around operators (Gecode uses ' '
53 /// for parsing logbrancher while Chuffed uses them as a delimiter
54 /// between literals)
55
56 if (lhs.substr(0, 3) == "[i]" || lhs.substr(0, 3) == "[f]")
57 {
58 lhs = lhs.substr(3);
59 }
60
61 if (rhs.substr(0, 3) == "[i]" || rhs.substr(0, 3) == "[f]")
62 {
63 rhs = rhs.substr(3);
64 }
65
66 lhs.erase(remove_if(lhs.begin(), lhs.end(), isspace), lhs.end());
67 rhs.erase(remove_if(rhs.begin(), rhs.end(), isspace), rhs.end());
68
69 find_and_replace_all(lhs, "==", "=");
70 find_and_replace_all(rhs, "==", "=");
71
72 // qDebug() << "comparing: " << lhs.c_str() << " " << rhs.c_str();
73
74 if (lhs.compare(rhs) != 0)
75 {
76 return false;
77 }
78
79 return true;
80 }
81
compareNodes(NodeID n1,const NodeTree & nt1,NodeID n2,const NodeTree & nt2,bool with_labels)82 static bool compareNodes(NodeID n1, const NodeTree &nt1,
83 NodeID n2, const NodeTree &nt2,
84 bool with_labels)
85 {
86
87 if (n1 == NodeID::NoNode || n2 == NodeID::NoNode)
88 return false;
89
90 if (nt1.getStatus(n1) != nt2.getStatus(n2))
91 return false;
92
93 if (with_labels)
94 {
95 const auto &label1 = nt1.getLabel(n1);
96 const auto &label2 = nt2.getLabel(n2);
97 if (!labelsEqual(label1, label2))
98 return false;
99 }
100
101 return true;
102 }
103
104 /// Copy the subtree rooted at nid_s of nt_s as a subtree rooted at nid in nt
copy_tree_into(NodeTree & nt,NodeID nid,const NodeTree & nt_s,NodeID nid_s)105 static void copy_tree_into(NodeTree &nt, NodeID nid, const NodeTree &nt_s, NodeID nid_s)
106 {
107
108 QStack<NodeID> stack;
109 QStack<NodeID> stack_s;
110 stack.push(nid);
111 stack_s.push(nid_s);
112
113 while (stack_s.size() > 0)
114 {
115 auto node_s = stack_s.pop();
116 auto node = stack.pop();
117
118 auto kids = nt_s.childrenCount(node_s);
119 auto status = nt_s.getStatus(node_s);
120
121 /// TODO: make sure only the reference to a label is stored
122 auto label = nt_s.getLabel(node_s);
123
124 nt.promoteNode(node, kids, status, label);
125
126 for (auto alt = 0; alt < kids; ++alt)
127 {
128 stack.push(nt.getChild(node, alt));
129 stack_s.push(nt_s.getChild(node_s, alt));
130 }
131 }
132 }
133
create_pentagon(NodeTree & nt,NodeID nid,const NodeTree & nt_l,NodeID nid_l,const NodeTree & nt_r,NodeID nid_r)134 void create_pentagon(NodeTree &nt, NodeID nid,
135 const NodeTree &nt_l, NodeID nid_l,
136 const NodeTree &nt_r, NodeID nid_r)
137 {
138
139 nt.promoteNode(nid, 2, NodeStatus::MERGED);
140
141 /// copy the subtree of nt_l into target_l
142 if (nid_l != NodeID::NoNode)
143 {
144 auto target_l = nt.getChild(nid, 0);
145 copy_tree_into(nt, target_l, nt_l, nid_l);
146 }
147
148 /// copy the subtree of nt_r into target_r
149 if (nid_r != NodeID::NoNode)
150 {
151 auto target_r = nt.getChild(nid, 1);
152 copy_tree_into(nt, target_r, nt_r, nid_r);
153 }
154 }
155
run()156 void TreeMerger::run()
157 {
158
159 print("Merging: running...");
160
161 /// It is quite unlikely, but this can dead-lock (needs consistent ordering)
162 utils::MutexLocker locker_l(&tree_l.treeMutex());
163 utils::MutexLocker locker_r(&tree_r.treeMutex());
164 utils::MutexLocker locker_res(&res_tree->treeMutex());
165
166 QStack<NodeID> stack_l, stack_r, stack;
167
168 auto root_l = tree_l.getRoot();
169 auto root_r = tree_r.getRoot();
170
171 stack_l.push(root_l);
172 stack_r.push(root_r);
173
174 auto root = res_tree->createRoot(0);
175
176 stack.push(root);
177
178 while (stack_l.size() > 0)
179 {
180
181 auto node_l = stack_l.pop();
182 auto node_r = stack_r.pop();
183 auto target = stack.pop();
184
185 bool equal = compareNodes(node_l, tree_l, node_r, tree_r, false);
186
187 if (equal)
188 {
189
190 const auto kids_l = tree_l.childrenCount(node_l);
191 const auto kids_r = tree_r.childrenCount(node_r);
192
193 const auto min_kids = std::min(kids_l, kids_r);
194 const auto max_kids = std::max(kids_l, kids_r);
195
196 { /// The merged tree will always have the number of children of the 'larger' tree
197 auto status = tree_l.getStatus(node_l);
198 auto label = tree_l.getLabel(node_l);
199 res_tree->promoteNode(target, max_kids, status, label);
200 }
201
202 if (min_kids == max_kids)
203 {
204 /// ----- MERGE COMPLETELY -----
205 for (auto i = max_kids - 1; i >= 0; --i)
206 {
207 stack_l.push(tree_l.getChild(node_l, i));
208 stack_r.push(tree_r.getChild(node_r, i));
209 stack.push(res_tree->getChild(target, i));
210 }
211 }
212 else
213 {
214 /// ----- MERGE PARTIALLY -----
215
216 /// For every "extra" child
217 for (auto i = max_kids - 1; i >= min_kids; --i)
218 {
219
220 if (kids_l > kids_r)
221 {
222 const auto kid_l = tree_l.getChild(node_l, i);
223 const auto status = tree_l.getStatus(kid_l);
224
225 /// NOTE(maxim): this is most likely the case of replaying with skipped nodes,
226 /// so should not be compared (the same below)
227 if (status == NodeStatus::UNDETERMINED || status == NodeStatus::SKIPPED)
228 {
229 continue;
230 }
231
232 stack_l.push(kid_l);
233 stack_r.push(NodeID::NoNode);
234 stack.push(res_tree->getChild(target, i));
235 }
236 else
237 {
238 const auto kid_r = tree_r.getChild(node_r, i);
239 const auto status = tree_r.getStatus(kid_r);
240
241 if (status == NodeStatus::UNDETERMINED || status == NodeStatus::SKIPPED)
242 {
243 continue;
244 }
245
246 stack_l.push(NodeID::NoNode);
247 stack_r.push(kid_r);
248 stack.push(res_tree->getChild(target, i));
249 }
250 }
251
252 /// For every child in common
253 for (auto i = min_kids - 1; i >= 0; --i)
254 {
255 stack_l.push(tree_l.getChild(node_l, i));
256 stack_r.push(tree_r.getChild(node_r, i));
257 stack.push(res_tree->getChild(target, i));
258 }
259 }
260 }
261 else
262 {
263 create_pentagon(*res_tree, target, tree_l, node_l, tree_r, node_r);
264
265 auto count_left = utils::count_descendants(tree_l, node_l);
266 auto count_right = utils::count_descendants(tree_r, node_r);
267 auto pen_item = PentagonItem{target, count_left, count_right};
268
269 merge_result->push_back(pen_item);
270 }
271 }
272
273 print("Merging: done");
274 }
275
276 } // namespace analysis
277 } // namespace cpprofiler