blob: 7a2c4160b3d046a09938b2ec06f4feec3549c071 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.skyframe;
15
janakrfce927f2017-11-03 21:48:32 +010016import com.google.common.base.MoreObjects;
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000019import com.google.common.collect.Iterables;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.build.lib.collect.nestedset.NestedSet;
21import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
22import com.google.devtools.build.lib.collect.nestedset.Order;
23import com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import java.util.Collection;
shreyaxae24bfe2018-02-21 14:54:30 -080025import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import javax.annotation.Nullable;
27
28/**
29 * Information about why a {@link SkyValue} failed to evaluate successfully.
30 *
31 * <p>This is intended only for use in alternative {@code MemoizingEvaluator} implementations.
32 */
Michajlo Matijkiw4e29c832015-09-30 22:23:25 +000033public class ErrorInfo {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000035 /** Create an ErrorInfo from a {@link ReifiedSkyFunctionException}. */
Nathan Harmata97731682015-12-09 23:36:22 +000036 public static ErrorInfo fromException(ReifiedSkyFunctionException skyFunctionException,
37 boolean isTransitivelyTransient) {
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000038 SkyKey rootCauseSkyKey = skyFunctionException.getRootCauseSkyKey();
39 Exception rootCauseException = skyFunctionException.getCause();
40 return new ErrorInfo(
41 NestedSetBuilder.create(Order.STABLE_ORDER, rootCauseSkyKey),
42 Preconditions.checkNotNull(rootCauseException, "Cause null %s", rootCauseException),
43 rootCauseSkyKey,
44 /*cycles=*/ ImmutableList.<CycleInfo>of(),
nharmatabea67e92017-06-16 00:26:27 +020045 skyFunctionException.isTransient(),
Nathan Harmata97731682015-12-09 23:36:22 +000046 isTransitivelyTransient || skyFunctionException.isTransient(),
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000047 skyFunctionException.isCatastrophic());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048 }
49
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000050 /** Create an ErrorInfo from a {@link CycleInfo}. */
Michajlo Matijkiwe8f7f5e2015-09-30 02:39:27 +000051 public static ErrorInfo fromCycle(CycleInfo cycleInfo) {
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000052 return new ErrorInfo(
53 /*rootCauses=*/ NestedSetBuilder.<SkyKey>emptySet(Order.STABLE_ORDER),
54 /*exception=*/ null,
55 /*rootCauseOfException=*/ null,
56 ImmutableList.of(cycleInfo),
nharmatabea67e92017-06-16 00:26:27 +020057 /*isDirectlyTransient=*/ false,
58 /*isTransitivelyTransient=*/ false,
janakr4089f8b2018-05-22 20:08:41 -070059 /* isCatastrophic= */ false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060 }
61
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000062 /** Create an ErrorInfo from a collection of existing errors. */
63 public static ErrorInfo fromChildErrors(SkyKey currentValue, Collection<ErrorInfo> childErrors) {
64 Preconditions.checkNotNull(currentValue, "currentValue must not be null");
65 Preconditions.checkState(!childErrors.isEmpty(), "childErrors may not be empty");
66
67 NestedSetBuilder<SkyKey> rootCausesBuilder = NestedSetBuilder.stableOrder();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068 ImmutableList.Builder<CycleInfo> cycleBuilder = ImmutableList.builder();
69 Exception firstException = null;
70 SkyKey firstChildKey = null;
nharmatabea67e92017-06-16 00:26:27 +020071 boolean isTransitivelyTransient = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 boolean isCatastrophic = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073 for (ErrorInfo child : childErrors) {
74 if (firstException == null) {
Nathan Harmata97731682015-12-09 23:36:22 +000075 // Arbitrarily pick the first error.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076 firstException = child.getException();
77 firstChildKey = child.getRootCauseOfException();
78 }
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000079 rootCausesBuilder.addTransitive(child.rootCauses);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010080 cycleBuilder.addAll(CycleInfo.prepareCycles(currentValue, child.cycles));
nharmatabea67e92017-06-16 00:26:27 +020081 isTransitivelyTransient |= child.isTransitivelyTransient();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010082 isCatastrophic |= child.isCatastrophic();
83 }
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000084
85 return new ErrorInfo(
86 rootCausesBuilder.build(),
87 firstException,
88 firstChildKey,
89 cycleBuilder.build(),
nharmatabea67e92017-06-16 00:26:27 +020090 /*isDirectlyTransient=*/ false,
91 isTransitivelyTransient,
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000092 isCatastrophic);
93 }
94
Michajlo Matijkiw7241fd62015-10-05 18:08:57 +000095 private final NestedSet<SkyKey> rootCauses;
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000096
97 @Nullable private final Exception exception;
98 private final SkyKey rootCauseOfException;
99
100 private final ImmutableList<CycleInfo> cycles;
101
nharmatabea67e92017-06-16 00:26:27 +0200102 private final boolean isDirectlyTransient;
103 private final boolean isTransitivelyTransient;
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000104 private final boolean isCatastrophic;
105
nharmatabea67e92017-06-16 00:26:27 +0200106 public ErrorInfo(
107 NestedSet<SkyKey> rootCauses,
108 @Nullable Exception exception,
109 SkyKey rootCauseOfException,
110 ImmutableList<CycleInfo> cycles,
111 boolean isDirectlyTransient,
112 boolean isTransitivelyTransient,
janakr4089f8b2018-05-22 20:08:41 -0700113 boolean isCatastrophic) {
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000114 Preconditions.checkState(exception != null || !Iterables.isEmpty(cycles),
115 "At least one of exception and cycles must be non-null/empty, respectively");
116 Preconditions.checkState((exception == null) == (rootCauseOfException == null),
117 "exception and rootCauseOfException must both be null or non-null, got %s %s",
118 exception, rootCauseOfException);
119
120 this.rootCauses = rootCauses;
121 this.exception = exception;
122 this.rootCauseOfException = rootCauseOfException;
123 this.cycles = cycles;
nharmatabea67e92017-06-16 00:26:27 +0200124 this.isDirectlyTransient = isDirectlyTransient;
125 this.isTransitivelyTransient = isTransitivelyTransient;
janakr4089f8b2018-05-22 20:08:41 -0700126 this.isCatastrophic = isCatastrophic;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 }
128
129 @Override
shreyaxae24bfe2018-02-21 14:54:30 -0800130 public boolean equals(Object obj) {
131 if (this == obj) {
132 return true;
133 }
134 if (!(obj instanceof ErrorInfo)) {
135 return false;
136 }
137
138 ErrorInfo other = (ErrorInfo) obj;
139 if (rootCauses != other.rootCauses) {
140 if (rootCauses == null || other.rootCauses == null) {
141 return false;
142 }
143 if (!rootCauses.shallowEquals(other.rootCauses)) {
144 return false;
145 }
146 }
147
148 if (!Objects.equals(cycles, other.cycles)) {
149 return false;
150 }
151
152 // Don't check the specific exception as most exceptions don't implement equality but at least
153 // check their types and messages are the same.
154 if (exception != other.exception) {
155 if (exception == null || other.exception == null) {
156 return false;
157 }
158 // Class objects are singletons with a single class loader.
159 if (exception.getClass() != other.exception.getClass()) {
160 return false;
161 }
162 if (!Objects.equals(exception.getMessage(), other.exception.getMessage())) {
163 return false;
164 }
165 }
166
167 if (!Objects.equals(rootCauseOfException, other.rootCauseOfException)) {
168 return false;
169 }
170
171 return isDirectlyTransient == other.isDirectlyTransient
172 && isTransitivelyTransient == other.isTransitivelyTransient
173 && isCatastrophic == other.isCatastrophic;
174 }
175
176 @Override
177 public int hashCode() {
178 return Objects.hash(
179 exception == null ? null : exception.getClass(),
180 exception == null ? "" : exception.getMessage(),
181 rootCauseOfException,
182 cycles,
183 isDirectlyTransient,
184 isTransitivelyTransient,
185 isCatastrophic,
186 rootCauses == null ? 0 : rootCauses.shallowHashCode());
187 }
188
189 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100190 public String toString() {
janakrfce927f2017-11-03 21:48:32 +0100191 return MoreObjects.toStringHelper(this)
192 .add("exception", exception)
193 .add("rootCauses", rootCauses)
194 .add("cycles", cycles)
195 .add("isCatastrophic", isCatastrophic)
196 .add("rootCauseOfException", rootCauseOfException)
197 .add("isDirectlyTransient", isDirectlyTransient)
198 .add("isTransitivelyTransient", isTransitivelyTransient)
199 .toString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 }
201
202 /**
203 * The root causes of a value that failed to build are its descendant values that failed to build.
204 * If a value's descendants all built successfully, but it failed to, its root cause will be
205 * itself. If a value depends on a cycle, but has no other errors, this method will return
206 * the empty set.
207 */
208 public Iterable<SkyKey> getRootCauses() {
209 return rootCauses;
210 }
211
212 /**
213 * The exception thrown when building a value. May be null if value's only error is depending
214 * on a cycle.
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000215 *
216 * <p>The exception is used for reporting and thus may ultimately be rethrown by the caller.
217 * As well, during a --nokeep_going evaluation, if an error value is encountered from an earlier
218 * --keep_going build, the exception to be thrown is taken from here.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100219 */
220 @Nullable public Exception getException() {
221 return exception;
222 }
223
224 public SkyKey getRootCauseOfException() {
225 return rootCauseOfException;
226 }
227
228 /**
229 * Any cycles found when building this value.
230 *
231 * <p>If there are a large number of cycles, only a limited number are returned here.
232 *
233 * <p>If this value has a child through which there are multiple paths to the same cycle, only one
234 * path is returned here. However, if there are multiple paths to the same cycle, each of which
235 * goes through a different child, each of them is returned here.
236 */
janakr4089f8b2018-05-22 20:08:41 -0700237 public ImmutableList<CycleInfo> getCycleInfo() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100238 return cycles;
239 }
240
241 /**
nharmatabea67e92017-06-16 00:26:27 +0200242 * Returns true iff the error is directly transient, i.e. if there was a transient error
243 * encountered during the computation itself.
244 */
245 public boolean isDirectlyTransient() {
246 return isDirectlyTransient;
247 }
248
249 /**
Nathan Harmata97731682015-12-09 23:36:22 +0000250 * Returns true iff the error is transitively transient, i.e. if retrying the same computation
251 * could lead to a different result.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100252 */
nharmatabea67e92017-06-16 00:26:27 +0200253 public boolean isTransitivelyTransient() {
254 return isTransitivelyTransient;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100255 }
256
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100257 /**
258 * Returns true iff the error is catastrophic, i.e. it should halt even for a keepGoing update()
259 * call.
260 */
261 public boolean isCatastrophic() {
262 return isCatastrophic;
263 }
Janak Ramakrishnandad0a102015-09-18 20:59:28 +0000264
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100265}