compiler_wrapper: work around gcc failing due to a kernel bug
Very rarely on old GCCs, we'll complaints about GCC itself being handed
ERESTARTSYS. Retry the compilation in those cases. Similarly, we'll
sometimes see `run()` give us random errors with things like `waitid()`,
so handle those.
BUG=chromium:1166017
TEST=CQ
Change-Id: If9b6fdc523f60719608739da0eefa94c82164ae7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2648090
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 21a308b..009cc87 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -6,6 +6,7 @@
import (
"bytes"
+ "errors"
"fmt"
"io"
"path/filepath"
@@ -74,6 +75,8 @@
env = mainBuilder.env
var compilerCmd *command
clangSyntax := processClangSyntaxFlag(mainBuilder)
+
+ workAroundKernelBugWithRetries := false
if cfg.isAndroidWrapper {
mainBuilder.path = calculateAndroidWrapperPath(mainBuilder.path, mainBuilder.absWrapperPath)
switch mainBuilder.target.compilerType {
@@ -139,6 +142,7 @@
if err != nil {
return 0, err
}
+ workAroundKernelBugWithRetries = true
}
}
@@ -174,26 +178,75 @@
}
}
- commitRusage, err := maybeCaptureRusage(env, rusageLogfileName, compilerCmd, func(willLogRusage bool) error {
- var err error
- if willLogRusage {
- err = env.run(compilerCmd, env.stdin(), env.stdout(), env.stderr())
- } else {
- // Note: We return from this in non-fatal circumstances only if the
- // underlying env is not really doing an exec, e.g. commandRecordingEnv.
- err = env.exec(compilerCmd)
+ errRetryCompilation := errors.New("compilation retry requested")
+ var runCompiler func(willLogRusage bool) (int, error)
+ if !workAroundKernelBugWithRetries {
+ runCompiler = func(willLogRusage bool) (int, error) {
+ var err error
+ if willLogRusage {
+ err = env.run(compilerCmd, env.stdin(), env.stdout(), env.stderr())
+ } else {
+ // Note: We return from this in non-fatal circumstances only if the
+ // underlying env is not really doing an exec, e.g. commandRecordingEnv.
+ err = env.exec(compilerCmd)
+ }
+ return wrapSubprocessErrorWithSourceLoc(compilerCmd, err)
}
- exitCode, err = wrapSubprocessErrorWithSourceLoc(compilerCmd, err)
- return err
- })
- if err != nil {
- return exitCode, err
- }
- if err := commitRusage(exitCode); err != nil {
- return exitCode, fmt.Errorf("commiting rusage: %v", err)
+ } else {
+ getStdin, err := prebufferStdinIfNeeded(env, compilerCmd)
+ if err != nil {
+ return 0, wrapErrorwithSourceLocf(err, "prebuffering stdin: %v", err)
+ }
+
+ stdoutBuffer := &bytes.Buffer{}
+ stderrBuffer := &bytes.Buffer{}
+ retryAttempt := 0
+ runCompiler = func(willLogRusage bool) (int, error) {
+ retryAttempt++
+ stdoutBuffer.Reset()
+ stderrBuffer.Reset()
+
+ exitCode, compilerErr := wrapSubprocessErrorWithSourceLoc(compilerCmd,
+ env.run(compilerCmd, getStdin(), stdoutBuffer, stderrBuffer))
+
+ if compilerErr != nil || exitCode != 0 {
+ if retryAttempt < kernelBugRetryLimit && (errorContainsTracesOfKernelBug(compilerErr) || containsTracesOfKernelBug(stdoutBuffer.Bytes()) || containsTracesOfKernelBug(stderrBuffer.Bytes())) {
+ return exitCode, errRetryCompilation
+ }
+ }
+ _, stdoutErr := stdoutBuffer.WriteTo(env.stdout())
+ _, stderrErr := stderrBuffer.WriteTo(env.stderr())
+ if stdoutErr != nil {
+ return exitCode, wrapErrorwithSourceLocf(err, "writing stdout: %v", stdoutErr)
+ }
+ if stderrErr != nil {
+ return exitCode, wrapErrorwithSourceLocf(err, "writing stderr: %v", stderrErr)
+ }
+ return exitCode, compilerErr
+ }
}
- return exitCode, err
+ for {
+ var exitCode int
+ commitRusage, err := maybeCaptureRusage(env, rusageLogfileName, compilerCmd, func(willLogRusage bool) error {
+ var err error
+ exitCode, err = runCompiler(willLogRusage)
+ return err
+ })
+
+ switch {
+ case err == errRetryCompilation:
+ // Loop around again.
+ case err != nil:
+ return exitCode, err
+ default:
+ if err := commitRusage(exitCode); err != nil {
+ return exitCode, fmt.Errorf("commiting rusage: %v", err)
+ }
+
+ return exitCode, err
+ }
+ }
}
func prepareClangCommand(builder *commandBuilder) (err error) {