1// 2// Copyright (C) 2012 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#include "update_engine/payload_generator/cycle_breaker.h" 18 19#include <inttypes.h> 20 21#include <set> 22#include <string> 23#include <utility> 24 25#include <base/strings/string_util.h> 26#include <base/strings/stringprintf.h> 27 28#include "update_engine/common/utils.h" 29#include "update_engine/payload_generator/graph_utils.h" 30#include "update_engine/payload_generator/tarjan.h" 31 32using std::make_pair; 33using std::set; 34using std::vector; 35 36namespace chromeos_update_engine { 37 38// This is the outer function from the original paper. 39void CycleBreaker::BreakCycles(const Graph& graph, set<Edge>* out_cut_edges) { 40 cut_edges_.clear(); 41 42 // Make a copy, which we will modify by removing edges. Thus, in each 43 // iteration subgraph_ is the current subgraph or the original with 44 // vertices we desire. This variable was "A_K" in the original paper. 45 subgraph_ = graph; 46 47 // The paper calls for the "adjacency structure (i.e., graph) of 48 // strong (-ly connected) component K with least vertex in subgraph 49 // induced by {s, s + 1, ..., n}". 50 // We arbitrarily order each vertex by its index in the graph. Thus, 51 // each iteration, we are looking at the subgraph {s, s + 1, ..., n} 52 // and looking for the strongly connected component with vertex s. 53 54 TarjanAlgorithm tarjan; 55 skipped_ops_ = 0; 56 57 for (Graph::size_type i = 0; i < subgraph_.size(); i++) { 58 InstallOperation_Type op_type = graph[i].aop.op.type(); 59 if (op_type == InstallOperation::REPLACE || 60 op_type == InstallOperation::REPLACE_BZ) { 61 skipped_ops_++; 62 continue; 63 } 64 65 if (i > 0) { 66 // Erase node (i - 1) from subgraph_. First, erase what it points to 67 subgraph_[i - 1].out_edges.clear(); 68 // Now, erase any pointers to node (i - 1) 69 for (Graph::size_type j = i; j < subgraph_.size(); j++) { 70 subgraph_[j].out_edges.erase(i - 1); 71 } 72 } 73 74 // Calculate SCC (strongly connected component) with vertex i. 75 vector<Vertex::Index> component_indexes; 76 tarjan.Execute(i, &subgraph_, &component_indexes); 77 78 // Set subgraph edges for the components in the SCC. 79 for (vector<Vertex::Index>::iterator it = component_indexes.begin(); 80 it != component_indexes.end(); ++it) { 81 subgraph_[*it].subgraph_edges.clear(); 82 for (vector<Vertex::Index>::iterator jt = component_indexes.begin(); 83 jt != component_indexes.end(); ++jt) { 84 // If there's a link from *it -> *jt in the graph, 85 // add a subgraph_ edge 86 if (utils::MapContainsKey(subgraph_[*it].out_edges, *jt)) 87 subgraph_[*it].subgraph_edges.insert(*jt); 88 } 89 } 90 91 current_vertex_ = i; 92 blocked_.clear(); 93 blocked_.resize(subgraph_.size()); 94 blocked_graph_.clear(); 95 blocked_graph_.resize(subgraph_.size()); 96 Circuit(current_vertex_, 0); 97 } 98 99 out_cut_edges->swap(cut_edges_); 100 LOG(INFO) << "Cycle breaker skipped " << skipped_ops_ << " ops."; 101 DCHECK(stack_.empty()); 102} 103 104static const size_t kMaxEdgesToConsider = 2; 105 106void CycleBreaker::HandleCircuit() { 107 stack_.push_back(current_vertex_); 108 CHECK_GE(stack_.size(), 109 static_cast<vector<Vertex::Index>::size_type>(2)); 110 Edge min_edge = make_pair(stack_[0], stack_[1]); 111 uint64_t min_edge_weight = std::numeric_limits<uint64_t>::max(); 112 size_t edges_considered = 0; 113 for (vector<Vertex::Index>::const_iterator it = stack_.begin(); 114 it != (stack_.end() - 1); ++it) { 115 Edge edge = make_pair(*it, *(it + 1)); 116 if (cut_edges_.find(edge) != cut_edges_.end()) { 117 stack_.pop_back(); 118 return; 119 } 120 uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge); 121 if (edge_weight < min_edge_weight) { 122 min_edge_weight = edge_weight; 123 min_edge = edge; 124 } 125 edges_considered++; 126 if (edges_considered == kMaxEdgesToConsider) 127 break; 128 } 129 cut_edges_.insert(min_edge); 130 stack_.pop_back(); 131} 132 133void CycleBreaker::Unblock(Vertex::Index u) { 134 blocked_[u] = false; 135 136 for (Vertex::EdgeMap::iterator it = blocked_graph_[u].out_edges.begin(); 137 it != blocked_graph_[u].out_edges.end(); ) { 138 Vertex::Index w = it->first; 139 blocked_graph_[u].out_edges.erase(it++); 140 if (blocked_[w]) 141 Unblock(w); 142 } 143} 144 145bool CycleBreaker::StackContainsCutEdge() const { 146 for (vector<Vertex::Index>::const_iterator it = ++stack_.begin(), 147 e = stack_.end(); it != e; ++it) { 148 Edge edge = make_pair(*(it - 1), *it); 149 if (utils::SetContainsKey(cut_edges_, edge)) { 150 return true; 151 } 152 } 153 return false; 154} 155 156bool CycleBreaker::Circuit(Vertex::Index vertex, Vertex::Index depth) { 157 // "vertex" was "v" in the original paper. 158 bool found = false; // Was "f" in the original paper. 159 stack_.push_back(vertex); 160 blocked_[vertex] = true; 161 { 162 static int counter = 0; 163 counter++; 164 if (counter == 10000) { 165 counter = 0; 166 std::string stack_str; 167 for (Vertex::Index index : stack_) { 168 stack_str += std::to_string(index); 169 stack_str += " -> "; 170 } 171 LOG(INFO) << "stack: " << stack_str; 172 } 173 } 174 175 for (Vertex::SubgraphEdgeMap::iterator w = 176 subgraph_[vertex].subgraph_edges.begin(); 177 w != subgraph_[vertex].subgraph_edges.end(); ++w) { 178 if (*w == current_vertex_) { 179 // The original paper called for printing stack_ followed by 180 // current_vertex_ here, which is a cycle. Instead, we call 181 // HandleCircuit() to break it. 182 HandleCircuit(); 183 found = true; 184 } else if (!blocked_[*w]) { 185 if (Circuit(*w, depth + 1)) { 186 found = true; 187 if ((depth > kMaxEdgesToConsider) || StackContainsCutEdge()) 188 break; 189 } 190 } 191 } 192 193 if (found) { 194 Unblock(vertex); 195 } else { 196 for (Vertex::SubgraphEdgeMap::iterator w = 197 subgraph_[vertex].subgraph_edges.begin(); 198 w != subgraph_[vertex].subgraph_edges.end(); ++w) { 199 if (blocked_graph_[*w].out_edges.find(vertex) == 200 blocked_graph_[*w].out_edges.end()) { 201 blocked_graph_[*w].out_edges.insert(make_pair(vertex, 202 EdgeProperties())); 203 } 204 } 205 } 206 CHECK_EQ(vertex, stack_.back()); 207 stack_.pop_back(); 208 return found; 209} 210 211} // namespace chromeos_update_engine 212