Search in sources :

Example 6 with ReifiedSkyFunctionException

use of com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException in project bazel by bazelbuild.

the class ParallelEvaluator method bubbleErrorUp.

/**
   * Walk up graph to find a top-level node (without parents) that wanted this failure. Store the
   * failed nodes along the way in a map, with ErrorInfos that are appropriate for that layer.
   * Example:
   *
   * <pre>
   *                      foo   bar
   *                        \   /
   *           unrequested   baz
   *                     \    |
   *                      failed-node
   * </pre>
   *
   * User requests foo, bar. When failed-node fails, we look at its parents. unrequested is not
   * in-flight, so we replace failed-node by baz and repeat. We look at baz's parents. foo is
   * in-flight, so we replace baz by foo. Since foo is a top-level node and doesn't have parents, we
   * then break, since we know a top-level node, foo, that depended on the failed node.
   *
   * <p>There's the potential for a weird "track jump" here in the case:
   *
   * <pre>
   *                        foo
   *                       / \
   *                   fail1 fail2
   * </pre>
   *
   * If fail1 and fail2 fail simultaneously, fail2 may start propagating up in the loop below.
   * However, foo requests fail1 first, and then throws an exception based on that. This is not
   * incorrect, but may be unexpected.
   *
   * <p>Returns a map of errors that have been constructed during the bubbling up, so that the
   * appropriate error can be returned to the caller, even though that error was not written to the
   * graph. If a cycle is detected during the bubbling, this method aborts and returns null so that
   * the normal cycle detection can handle the cycle.
   *
   * <p>Note that we are not propagating error to the first top-level node but to the highest one,
   * because during this process we can add useful information about error from other nodes.
   */
private Map<SkyKey, ValueWithMetadata> bubbleErrorUp(final ErrorInfo leafFailure, SkyKey errorKey, Iterable<SkyKey> skyKeys) throws InterruptedException {
    Set<SkyKey> rootValues = ImmutableSet.copyOf(skyKeys);
    ErrorInfo error = leafFailure;
    Map<SkyKey, ValueWithMetadata> bubbleErrorInfo = new HashMap<>();
    boolean externalInterrupt = false;
    while (true) {
        NodeEntry errorEntry = Preconditions.checkNotNull(graph.get(null, Reason.ERROR_BUBBLING, errorKey), errorKey);
        Iterable<SkyKey> reverseDeps = errorEntry.isDone() ? errorEntry.getReverseDeps() : errorEntry.getInProgressReverseDeps();
        // We should break from loop only when node doesn't have any parents.
        if (Iterables.isEmpty(reverseDeps)) {
            Preconditions.checkState(rootValues.contains(errorKey), "Current key %s has to be a top-level key: %s", errorKey, rootValues);
            break;
        }
        SkyKey parent = null;
        NodeEntry parentEntry = null;
        for (SkyKey bubbleParent : reverseDeps) {
            if (bubbleErrorInfo.containsKey(bubbleParent)) {
                // We are in a cycle. Don't try to bubble anything up -- cycle detection will kick in.
                return null;
            }
            NodeEntry bubbleParentEntry = Preconditions.checkNotNull(graph.get(errorKey, Reason.ERROR_BUBBLING, bubbleParent), "parent %s of %s not in graph", bubbleParent, errorKey);
            // Might be the parent that requested the error.
            if (bubbleParentEntry.isDone()) {
                // This parent is cached from a previous evaluate call. We shouldn't bubble up to it
                // since any error message produced won't be meaningful to this evaluate call.
                // The child error must also be cached from a previous build.
                Preconditions.checkState(errorEntry.isDone(), "%s %s", errorEntry, bubbleParentEntry);
                Version parentVersion = bubbleParentEntry.getVersion();
                Version childVersion = errorEntry.getVersion();
                Preconditions.checkState(childVersion.atMost(evaluatorContext.getGraphVersion()) && !childVersion.equals(evaluatorContext.getGraphVersion()), "child entry is not older than the current graph version, but had a done parent. " + "child: %s childEntry: %s, childVersion: %s" + "bubbleParent: %s bubbleParentEntry: %s, parentVersion: %s, graphVersion: %s", errorKey, errorEntry, childVersion, bubbleParent, bubbleParentEntry, parentVersion, evaluatorContext.getGraphVersion());
                Preconditions.checkState(parentVersion.atMost(evaluatorContext.getGraphVersion()) && !parentVersion.equals(evaluatorContext.getGraphVersion()), "parent entry is not older than the current graph version. " + "child: %s childEntry: %s, childVersion: %s" + "bubbleParent: %s bubbleParentEntry: %s, parentVersion: %s, graphVersion: %s", errorKey, errorEntry, childVersion, bubbleParent, bubbleParentEntry, parentVersion, evaluatorContext.getGraphVersion());
                continue;
            }
            if (evaluatorContext.getProgressReceiver().isInflight(bubbleParent) && bubbleParentEntry.getTemporaryDirectDeps().expensiveContains(errorKey)) {
                // Only bubble up to parent if it's part of this build. If this node was dirtied and
                // re-evaluated, but in a build without this parent, we may try to bubble up to that
                // parent. Don't -- it's not part of the build.
                // Similarly, the parent may not yet have requested this dep in its dirtiness-checking
                // process. Don't bubble up to it in that case either.
                parent = bubbleParent;
                parentEntry = bubbleParentEntry;
                break;
            }
        }
        if (parent == null) {
            Preconditions.checkState(rootValues.contains(errorKey), "Current key %s has to be a top-level key: %s, %s", errorKey, rootValues, errorEntry);
            break;
        }
        Preconditions.checkNotNull(parentEntry, "%s %s", errorKey, parent);
        errorKey = parent;
        SkyFunction factory = evaluatorContext.getSkyFunctions().get(parent.functionName());
        if (parentEntry.isDirty()) {
            switch(parentEntry.getDirtyState()) {
                case CHECK_DEPENDENCIES:
                    // If this value's child was bubbled up to, it did not signal this value, and so we must
                    // manually make it ready to build.
                    parentEntry.signalDep();
                // Fall through to NEEDS_REBUILDING, since state is now NEEDS_REBUILDING.
                case NEEDS_REBUILDING:
                    maybeMarkRebuilding(parentEntry);
                // Fall through to REBUILDING.
                case REBUILDING:
                    break;
                default:
                    throw new AssertionError(parent + " not in valid dirty state: " + parentEntry);
            }
        }
        SkyFunctionEnvironment env = new SkyFunctionEnvironment(parent, new GroupedList<SkyKey>(), bubbleErrorInfo, ImmutableSet.<SkyKey>of(), evaluatorContext);
        externalInterrupt = externalInterrupt || Thread.currentThread().isInterrupted();
        try {
            // This build is only to check if the parent node can give us a better error. We don't
            // care about a return value.
            factory.compute(parent, env);
        } catch (InterruptedException interruptedException) {
        // Do nothing.
        // This throw happens if the builder requested the failed node, and then checked the
        // interrupted state later -- getValueOrThrow sets the interrupted bit after the failed
        // value is requested, to prevent the builder from doing too much work.
        } catch (SkyFunctionException builderException) {
            // Clear interrupted status. We're not listening to interrupts here.
            Thread.interrupted();
            ReifiedSkyFunctionException reifiedBuilderException = new ReifiedSkyFunctionException(builderException, parent);
            if (reifiedBuilderException.getRootCauseSkyKey().equals(parent)) {
                error = ErrorInfo.fromException(reifiedBuilderException, /*isTransitivelyTransient=*/
                false);
                bubbleErrorInfo.put(errorKey, ValueWithMetadata.error(ErrorInfo.fromChildErrors(errorKey, ImmutableSet.of(error)), env.buildEvents(parentEntry, /*missingChildren=*/
                true)));
                continue;
            }
        } finally {
            // Clear interrupted status. We're not listening to interrupts here.
            Thread.interrupted();
        }
        // Builder didn't throw an exception, so just propagate this one up.
        bubbleErrorInfo.put(errorKey, ValueWithMetadata.error(ErrorInfo.fromChildErrors(errorKey, ImmutableSet.of(error)), env.buildEvents(parentEntry, /*missingChildren=*/
        true)));
    }
    // bubbling node calls getValueOrThrow() on a node in error.
    if (externalInterrupt) {
        Thread.currentThread().interrupt();
    }
    return bubbleErrorInfo;
}
Also used : HashMap(java.util.HashMap) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException)

Example 7 with ReifiedSkyFunctionException

use of com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException in project bazel by bazelbuild.

the class ErrorInfoTest method testFromChildErrors.

@Test
public void testFromChildErrors() {
    CycleInfo cycle = new CycleInfo(ImmutableList.of(SkyKey.create(SkyFunctionName.create("PATH"), 1234)), ImmutableList.of(SkyKey.create(SkyFunctionName.create("CYCLE"), 4321)));
    ErrorInfo cycleErrorInfo = ErrorInfo.fromCycle(cycle);
    Exception exception1 = new IOException("ehhhhh");
    SkyKey causeOfException1 = SkyKey.create(SkyFunctionName.create("CAUSE1"), 1234);
    DummySkyFunctionException dummyException1 = new DummySkyFunctionException(exception1, /*isTransient=*/
    true, /*isCatastrophic=*/
    false);
    ErrorInfo exceptionErrorInfo1 = ErrorInfo.fromException(new ReifiedSkyFunctionException(dummyException1, causeOfException1), /*isTransitivelyTransient=*/
    false);
    // N.B this ErrorInfo will be catastrophic.
    Exception exception2 = new IOException("blahhhhh");
    SkyKey causeOfException2 = SkyKey.create(SkyFunctionName.create("CAUSE2"), 5678);
    DummySkyFunctionException dummyException2 = new DummySkyFunctionException(exception2, /*isTransient=*/
    false, /*isCatastrophic=*/
    true);
    ErrorInfo exceptionErrorInfo2 = ErrorInfo.fromException(new ReifiedSkyFunctionException(dummyException2, causeOfException2), /*isTransitivelyTransient=*/
    false);
    SkyKey currentKey = SkyKey.create(SkyFunctionName.create("CURRENT"), 9876);
    ErrorInfo errorInfo = ErrorInfo.fromChildErrors(currentKey, ImmutableList.of(cycleErrorInfo, exceptionErrorInfo1, exceptionErrorInfo2));
    assertThat(errorInfo.getRootCauses()).containsExactly(causeOfException1, causeOfException2);
    // For simplicity we test the current implementation detail that we choose the first non-null
    // (exception, cause) pair that we encounter. This isn't necessarily a requirement of the
    // interface, but it makes the test convenient and is a way to document the current behavior.
    assertThat(errorInfo.getException()).isSameAs(exception1);
    assertThat(errorInfo.getRootCauseOfException()).isSameAs(causeOfException1);
    assertThat(errorInfo.getCycleInfo()).containsExactly(new CycleInfo(ImmutableList.of(currentKey, Iterables.getOnlyElement(cycle.getPathToCycle())), cycle.getCycle()));
    assertThat(errorInfo.isTransient()).isTrue();
    assertThat(errorInfo.isCatastrophic()).isTrue();
}
Also used : IOException(java.io.IOException) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException) IOException(java.io.IOException) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException) Test(org.junit.Test)

Example 8 with ReifiedSkyFunctionException

use of com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException in project bazel by bazelbuild.

the class InMemoryNodeEntryTest method noPruneWhenDetailsChange.

@Test
public void noPruneWhenDetailsChange() throws InterruptedException {
    NodeEntry entry = new InMemoryNodeEntry();
    // Start evaluation.
    entry.addReverseDepAndCheckIfDone(null);
    SkyKey dep = key("dep");
    addTemporaryDirectDep(entry, dep);
    entry.signalDep();
    setValue(entry, new IntegerValue(5), /*errorInfo=*/
    null, /*graphVersion=*/
    0L);
    assertFalse(entry.isDirty());
    assertTrue(entry.isDone());
    entry.markDirty(/*isChanged=*/
    false);
    assertTrue(entry.isDirty());
    assertFalse(entry.isChanged());
    assertFalse(entry.isDone());
    assertThatNodeEntry(entry).addReverseDepAndCheckIfDone(null).isEqualTo(DependencyState.NEEDS_SCHEDULING);
    assertTrue(entry.isReady());
    SkyKey parent = key("parent");
    entry.addReverseDepAndCheckIfDone(parent);
    assertEquals(NodeEntry.DirtyState.CHECK_DEPENDENCIES, entry.getDirtyState());
    assertThat(entry.getNextDirtyDirectDeps()).containsExactly(dep);
    addTemporaryDirectDep(entry, dep);
    entry.signalDep(IntVersion.of(1L));
    assertEquals(NodeEntry.DirtyState.NEEDS_REBUILDING, entry.getDirtyState());
    assertThatNodeEntry(entry).hasTemporaryDirectDepsThat().containsExactly(dep);
    ReifiedSkyFunctionException exception = new ReifiedSkyFunctionException(new GenericFunctionException(new SomeErrorException("oops"), Transience.PERSISTENT), key("cause"));
    entry.markRebuilding();
    setValue(entry, new IntegerValue(5), ErrorInfo.fromException(exception, false), /*graphVersion=*/
    1L);
    assertTrue(entry.isDone());
    assertEquals("Version increments when setValue changes", IntVersion.of(1), entry.getVersion());
}
Also used : NodeEntrySubjectFactory.assertThatNodeEntry(com.google.devtools.build.skyframe.NodeEntrySubjectFactory.assertThatNodeEntry) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException) Test(org.junit.Test)

Example 9 with ReifiedSkyFunctionException

use of com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException in project bazel by bazelbuild.

the class InMemoryNodeEntryTest method errorAndValue.

@Test
public void errorAndValue() throws InterruptedException {
    NodeEntry entry = new InMemoryNodeEntry();
    // Start evaluation.
    entry.addReverseDepAndCheckIfDone(null);
    ReifiedSkyFunctionException exception = new ReifiedSkyFunctionException(new GenericFunctionException(new SomeErrorException("oops"), Transience.PERSISTENT), key("cause"));
    ErrorInfo errorInfo = ErrorInfo.fromException(exception, false);
    setValue(entry, new SkyValue() {
    }, errorInfo, /*graphVersion=*/
    0L);
    assertTrue(entry.isDone());
    assertEquals(errorInfo, entry.getErrorInfo());
}
Also used : NodeEntrySubjectFactory.assertThatNodeEntry(com.google.devtools.build.skyframe.NodeEntrySubjectFactory.assertThatNodeEntry) ReifiedSkyFunctionException(com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException) Test(org.junit.Test)

Aggregations

ReifiedSkyFunctionException (com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException)9 Test (org.junit.Test)6 NodeEntrySubjectFactory.assertThatNodeEntry (com.google.devtools.build.skyframe.NodeEntrySubjectFactory.assertThatNodeEntry)5 IOException (java.io.IOException)2 HashMap (java.util.HashMap)1