diff options
author | Klaus Aehlig <aehlig@google.com> | 2018-07-27 02:06:00 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-07-27 02:07:00 -0700 |
commit | d0190d3503f3adb0655579312ee359c67291992d (patch) | |
tree | 12f0f87e6fff9c5a99f8004574cb46914c590166 /src | |
parent | 5168c50641b4c029075ed3e47ec81ccda848912b (diff) |
Experimental UI: on limited output, reserve capacity for post-build status
When the experimental UI is asked to limit its overall output, reserve a certain
fraction for output to be generated after the build is complete. In this way,
at least a final status, or a bit of the test summary, is shown.
Also, to help staying within the limit also for small limits, from a certain
threshold onwards, more severely restrict the output we allow for each individual
action.
Change-Id: I912aec0dd17639d9864133a10359f93537b473ad
PiperOrigin-RevId: 206288651
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java | 53 | ||||
-rwxr-xr-x | src/test/shell/integration/experimental_ui_test.sh | 27 |
2 files changed, 70 insertions, 10 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java index 3031fc2607..8285284711 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java @@ -103,6 +103,7 @@ public class ExperimentalEventHandler implements EventHandler { private byte[] stderrBuffer; private final long outputLimit; + private long reservedOutputCapacity; private final AtomicLong counter; /** * The following constants determine how the output limiting is done gracefully. They are all @@ -110,6 +111,7 @@ public class ExperimentalEventHandler implements EventHandler { * * <p>The degrading of progress updates to stay within output limit is done in the following * steps. + * * <ul> * <li>We limit progress updates to at most one per second; this is the granularity at which * times in the progress bar are shown. So the appearance won't look too bad. Hence we start @@ -121,24 +123,36 @@ public class ExperimentalEventHandler implements EventHandler { * <li>We start decreasing the update frequency to what we would do, if curses were not allowed. * Note that now the time between updates is at least a fixed fraction of the time that * passed so far; so the time between progress updates will continue to increase. + * <li>The last small fraction of the output, we reserve for a post-build status messages (in + * particular test summaries). * </ul> */ - private static final double CAPACITY_INCREASE_UPDATE_DELAY = 0.7; + private static final double CAPACITY_INCREASE_UPDATE_DELAY = 0.6; - private static final double CAPACITY_SHORT_PROGRESS_BAR = 0.5; - private static final double CAPACITY_UPDATE_DELAY_5_SECONDS = 0.4; - private static final double CAPACITY_UPDATE_DELAY_AS_NO_CURSES = 0.3; + private static final double CAPACITY_SHORT_PROGRESS_BAR = 0.4; + private static final double CAPACITY_UPDATE_DELAY_5_SECONDS = 0.3; + private static final double CAPACITY_UPDATE_DELAY_AS_NO_CURSES = 0.2; /** * The degrading of printing stdout/stderr is achieved by limiting the output for an individual * event if printing it fully would get us above the threshold. If limited, at most a given * fraction of the remaining capacity my be used by any such event; larger events are truncated to * their end (this is what the user would anyway only see on the terminal if the output is very * large). In any case, we always allow at least twice the terminal width, to make the output at - * least somewhat useful. + * least somewhat useful. From a given threshold onwards, we always restrict to at most twice the + * terminal width. + */ + private static final double CAPACITY_STRONG_LIMIT_OUT_ERR_EVENTS = 0.7; + private static final double CAPACITY_LIMIT_OUT_ERR_EVENTS = 0.5; + private static final double RELATIVE_OUT_ERR_LIMIT = 0.05; + + /** + * The reservation of output capacity for the final status is computed as follows: we always + * reserve at least a certain numer of lines, and at least a certain fraction of the overall + * capacity, to show more status in scenarios where we have a bigger limit. */ - private static final double CAPACITY_LIMIT_OUT_ERR_EVENTS = 0.6; + private static final long MINIMAL_POST_BUILD_OUTPUT_LINES = 12; - private static final double RELATIVE_OUT_ERR_LIMIT = 0.1; + private static final double MINIMAL_POST_BUILD_OUTPUT_CAPACITY = 0.05; public final int terminalWidth; @@ -184,6 +198,7 @@ public class ExperimentalEventHandler implements EventHandler { public ExperimentalEventHandler( OutErr outErr, BlazeCommandEventHandler.Options options, Clock clock) { + this.terminalWidth = (options.terminalColumns > 0 ? options.terminalColumns : 80); this.outputLimit = options.experimentalUiLimitConsoleOutput; this.counter = new AtomicLong(outputLimit); if (outputLimit > 0) { @@ -193,6 +208,10 @@ public class ExperimentalEventHandler implements EventHandler { outErr.getOutputStream(), this.counter), new FullyBufferedOutputStreamMaybeWithCounting( outErr.getErrorStream(), this.counter)); + reservedOutputCapacity = + Math.max( + MINIMAL_POST_BUILD_OUTPUT_LINES * this.terminalWidth, + Math.round(MINIMAL_POST_BUILD_OUTPUT_CAPACITY * outputLimit)); } else { // unlimited output; no need to count, but still fully buffer this.outErr = @@ -202,7 +221,6 @@ public class ExperimentalEventHandler implements EventHandler { } this.cursorControl = options.useCursorControl(); this.terminal = new AnsiTerminal(this.outErr.getErrorStream()); - this.terminalWidth = (options.terminalColumns > 0 ? options.terminalColumns : 80); this.showProgress = options.showProgress; this.progressInTermTitle = options.progressInTermTitle && options.useCursorControl(); this.showTimestamp = options.showTimestamp; @@ -247,7 +265,7 @@ public class ExperimentalEventHandler implements EventHandler { // how much we write. return 1.0; } - return (counter.get() - wantWrite) / (double) outputLimit; + return (counter.get() - wantWrite - reservedOutputCapacity) / (double) outputLimit; } private double remainingCapacity() { @@ -318,10 +336,17 @@ public class ExperimentalEventHandler implements EventHandler { stream.flush(); } else { byte[] message = event.getMessageBytes(); - if (remainingCapacity(message.length) < CAPACITY_LIMIT_OUT_ERR_EVENTS) { + double cap = remainingCapacity(message.length); + if (cap < 0) { + return; + } + if (cap < CAPACITY_LIMIT_OUT_ERR_EVENTS) { // Have to ensure the message is not too large. long allowedLength = Math.max(2 * terminalWidth, Math.round(RELATIVE_OUT_ERR_LIMIT * counter.get())); + if (cap < CAPACITY_STRONG_LIMIT_OUT_ERR_EVENTS) { + allowedLength = Math.min(allowedLength, 2 * terminalWidth); + } if (message.length > allowedLength) { // Have to truncate the message message = @@ -373,6 +398,10 @@ public class ExperimentalEventHandler implements EventHandler { if (incompleteLine) { crlf(); } + if (remainingCapacity() < 0) { + terminal.flush(); + return; + } if (showTimestamp) { terminal.writeString( TIMESTAMP_FORMAT.format( @@ -489,6 +518,7 @@ public class ExperimentalEventHandler implements EventHandler { boolean done = false; synchronized (this) { stateTracker.buildComplete(event); + reservedOutputCapacity = 0; ignoreRefreshLimitOnce(); refresh(); @@ -842,6 +872,9 @@ public class ExperimentalEventHandler implements EventHandler { } private synchronized void addProgressBar() throws IOException { + if (remainingCapacity() < 0) { + return; + } LineCountingAnsiTerminalWriter countingTerminalWriter = new LineCountingAnsiTerminalWriter(terminal); AnsiTerminalWriter terminalWriter = countingTerminalWriter; diff --git a/src/test/shell/integration/experimental_ui_test.sh b/src/test/shell/integration/experimental_ui_test.sh index 739559c061..be37b4563a 100755 --- a/src/test/shell/integration/experimental_ui_test.sh +++ b/src/test/shell/integration/experimental_ui_test.sh @@ -98,6 +98,15 @@ genrule( tools = [":do_output.sh"], cmd = "\$(location :do_output.sh) B && touch \$@", ) +sh_library( + name = "outputlib", + data = [":withOutputA", ":withOutputB"], +) +sh_test( + name = "truedependingonoutput", + srcs = ["true.sh"], + deps = [":outputlib"], +) EOF } @@ -303,4 +312,22 @@ function test_output_limit { || fail "Output too large, is ${output_length}" } +function test_status_despite_output_limit { + # Verify that even if we limit the output very strictly, we + # still find the test summary. + bazel clean --expunge + bazel version + bazel test --experimental_ui --curses=yes --color=yes \ + --experimental_ui_limit_console_output=500 \ + pkg:truedependingonoutput >$TEST_log 2>&1 \ + || fail "expected success" + expect_log "//pkg:truedependingonoutput.*PASSED" + + # Also sanity check that the limit was applied, again, allowing + # 2k for any startup messages etc generated by the client. + output_length=`cat $TEST_log | wc -c` + [ "${output_length}" -le 2724 ] \ + || fail "Output too large, is ${output_length}" +} + run_suite "Integration tests for ${PRODUCT_NAME}'s experimental UI" |