aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/compiler_wrapper.go
diff options
context:
space:
mode:
Diffstat (limited to 'compiler_wrapper/compiler_wrapper.go')
-rw-r--r--compiler_wrapper/compiler_wrapper.go223
1 files changed, 177 insertions, 46 deletions
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 2b9459e4..986eabab 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -6,10 +6,14 @@ package main
import (
"bytes"
+ "context"
+ "errors"
"fmt"
"io"
"path/filepath"
+ "strconv"
"strings"
+ "time"
)
func callCompiler(env env, cfg *config, inputCmd *command) int {
@@ -61,6 +65,50 @@ func calculateAndroidWrapperPath(mainBuilderPath string, absWrapperPath string)
return "." + string(filepath.Separator) + basePart
}
+func runAndroidClangTidy(env env, cmd *command) error {
+ timeout, found := env.getenv("TIDY_TIMEOUT")
+ if !found {
+ return env.exec(cmd)
+ }
+ seconds, err := strconv.Atoi(timeout)
+ if err != nil || seconds == 0 {
+ return env.exec(cmd)
+ }
+ getSourceFile := func() string {
+ // Note: This depends on Android build system's clang-tidy command line format.
+ // Last non-flag before "--" in cmd.Args is used as the source file name.
+ sourceFile := "unknown_file"
+ for _, arg := range cmd.Args {
+ if arg == "--" {
+ break
+ }
+ if strings.HasPrefix(arg, "-") {
+ continue
+ }
+ sourceFile = arg
+ }
+ return sourceFile
+ }
+ startTime := time.Now()
+ err = env.runWithTimeout(cmd, time.Duration(seconds)*time.Second)
+ if !errors.Is(err, context.DeadlineExceeded) {
+ // When used time is over half of TIDY_TIMEOUT, give a warning.
+ // These warnings allow users to fix slow jobs before they get worse.
+ usedSeconds := int(time.Now().Sub(startTime) / time.Second)
+ if usedSeconds > seconds/2 {
+ warning := "%s:1:1: warning: clang-tidy used %d seconds.\n"
+ fmt.Fprintf(env.stdout(), warning, getSourceFile(), usedSeconds)
+ }
+ return err
+ }
+ // When DeadllineExceeded, print warning messages.
+ warning := "%s:1:1: warning: clang-tidy aborted after %d seconds.\n"
+ fmt.Fprintf(env.stdout(), warning, getSourceFile(), seconds)
+ fmt.Fprintf(env.stdout(), "TIMEOUT: %s %s\n", cmd.Path, strings.Join(cmd.Args, " "))
+ // Do not stop Android build. Just give a warning and return no error.
+ return nil
+}
+
func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
if err := checkUnsupportedFlags(inputCmd); err != nil {
return 0, err
@@ -74,6 +122,15 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
env = mainBuilder.env
var compilerCmd *command
clangSyntax := processClangSyntaxFlag(mainBuilder)
+
+ rusageEnabled := isRusageEnabled(env)
+
+ // Disable CCache for rusage logs
+ // Note: Disabling Goma causes timeout related INFRA_FAILUREs in builders
+ allowCCache := !rusageEnabled
+ remoteBuildUsed := false
+
+ workAroundKernelBugWithRetries := false
if cfg.isAndroidWrapper {
mainBuilder.path = calculateAndroidWrapperPath(mainBuilder.path, mainBuilder.absWrapperPath)
switch mainBuilder.target.compilerType {
@@ -81,7 +138,9 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
mainBuilder.addPostUserArgs(mainBuilder.cfg.clangPostFlags...)
- if _, err := processGomaCccFlags(mainBuilder); err != nil {
+ inheritGomaFromEnv := true
+ // Android doesn't support rewrapper; don't try to use it.
+ if remoteBuildUsed, err = processGomaCccFlags(mainBuilder, inheritGomaFromEnv); err != nil {
return 0, err
}
compilerCmd = mainBuilder.build()
@@ -97,19 +156,18 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
if err != nil {
return 0, err
}
- allowCCache := true
if tidyMode != tidyModeNone {
allowCCache = false
- clangCmdWithoutGomaAndCCache := mainBuilder.build()
+ clangCmdWithoutRemoteBuildAndCCache := mainBuilder.build()
var err error
switch tidyMode {
case tidyModeTricium:
if cfg.triciumNitsDir == "" {
return 0, newErrorwithSourceLocf("tricium linting was requested, but no nits directory is configured")
}
- err = runClangTidyForTricium(env, clangCmdWithoutGomaAndCCache, cSrcFile, cfg.triciumNitsDir, tidyFlags, cfg.crashArtifactsDir)
+ err = runClangTidyForTricium(env, clangCmdWithoutRemoteBuildAndCCache, cSrcFile, cfg.triciumNitsDir, tidyFlags, cfg.crashArtifactsDir)
case tidyModeAll:
- err = runClangTidy(env, clangCmdWithoutGomaAndCCache, cSrcFile, tidyFlags)
+ err = runClangTidy(env, clangCmdWithoutRemoteBuildAndCCache, cSrcFile, tidyFlags)
default:
panic(fmt.Sprintf("Unknown tidy mode: %v", tidyMode))
}
@@ -118,64 +176,135 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
return 0, err
}
}
- if err := processGomaCCacheFlags(allowCCache, mainBuilder); err != nil {
+ if remoteBuildUsed, err = processRemoteBuildAndCCacheFlags(allowCCache, mainBuilder); err != nil {
return 0, err
}
compilerCmd = mainBuilder.build()
} else {
if clangSyntax {
- allowCCache := false
- clangCmd, err := calcClangCommand(allowCCache, mainBuilder.clone())
+ allowCCache = false
+ _, clangCmd, err := calcClangCommand(allowCCache, mainBuilder.clone())
if err != nil {
return 0, err
}
- gccCmd, err := calcGccCommand(mainBuilder)
+ _, gccCmd, err := calcGccCommand(rusageEnabled, mainBuilder)
if err != nil {
return 0, err
}
return checkClangSyntax(env, clangCmd, gccCmd)
}
- compilerCmd, err = calcGccCommand(mainBuilder)
+ remoteBuildUsed, compilerCmd, err = calcGccCommand(rusageEnabled, mainBuilder)
if err != nil {
return 0, err
}
+ workAroundKernelBugWithRetries = true
}
}
- rusageLogfileName := getRusageLogFilename(env)
+
bisectStage := getBisectStage(env)
- if shouldForceDisableWerror(env, cfg) {
- if rusageLogfileName != "" {
- return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
- }
+
+ if rusageEnabled {
+ compilerCmd = removeRusageFromCommand(compilerCmd)
+ }
+
+ if shouldForceDisableWerror(env, cfg, mainBuilder.target.compilerType) {
if bisectStage != "" {
return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
}
return doubleBuildWithWNoError(env, cfg, compilerCmd)
}
if shouldCompileWithFallback(env) {
- if rusageLogfileName != "" {
- return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
+ if rusageEnabled {
+ return 0, newUserErrorf("TOOLCHAIN_RUSAGE_OUTPUT is meaningless with ANDROID_LLVM_PREBUILT_COMPILER_PATH")
}
if bisectStage != "" {
- return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
+ return 0, newUserErrorf("BISECT_STAGE is meaningless with ANDROID_LLVM_PREBUILT_COMPILER_PATH")
}
return compileWithFallback(env, cfg, compilerCmd, mainBuilder.absWrapperPath)
}
- if rusageLogfileName != "" {
- if bisectStage != "" {
- return 0, newUserErrorf("BISECT_STAGE is meaningless with GETRUSAGE")
- }
- return logRusage(env, rusageLogfileName, compilerCmd)
- }
if bisectStage != "" {
+ if rusageEnabled {
+ return 0, newUserErrorf("TOOLCHAIN_RUSAGE_OUTPUT is meaningless with BISECT_STAGE")
+ }
compilerCmd, err = calcBisectCommand(env, cfg, bisectStage, compilerCmd)
if err != nil {
return 0, err
}
}
- // Note: We return an exit code only if the underlying env is not
- // really doing an exec, e.g. commandRecordingEnv.
- return wrapSubprocessErrorWithSourceLoc(compilerCmd, 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 if cfg.isAndroidWrapper && mainBuilder.target.compilerType == clangTidyType {
+ // Only clang-tidy has timeout feature now.
+ err = runAndroidClangTidy(env, compilerCmd)
+ } 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)
+ }
+ } 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
+ }
+ }
+
+ for {
+ var exitCode int
+ commitRusage, err := maybeCaptureRusage(env, 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 !remoteBuildUsed {
+ if err := commitRusage(exitCode); err != nil {
+ return exitCode, fmt.Errorf("commiting rusage: %v", err)
+ }
+ }
+ return exitCode, err
+ }
+ }
}
func prepareClangCommand(builder *commandBuilder) (err error) {
@@ -191,38 +320,40 @@ func prepareClangCommand(builder *commandBuilder) (err error) {
return processClangFlags(builder)
}
-func calcClangCommand(allowCCache bool, builder *commandBuilder) (*command, error) {
+func calcClangCommand(allowCCache bool, builder *commandBuilder) (bool, *command, error) {
err := prepareClangCommand(builder)
if err != nil {
- return nil, err
+ return false, nil, err
}
- if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
- return nil, err
+ remoteBuildUsed, err := processRemoteBuildAndCCacheFlags(allowCCache, builder)
+ if err != nil {
+ return remoteBuildUsed, nil, err
}
- return builder.build(), nil
+ return remoteBuildUsed, builder.build(), nil
}
-func calcGccCommand(builder *commandBuilder) (*command, error) {
+func calcGccCommand(enableRusage bool, builder *commandBuilder) (bool, *command, error) {
if !builder.cfg.isHostWrapper {
processSysrootFlag(builder)
}
builder.addPreUserArgs(builder.cfg.gccFlags...)
- if !builder.cfg.isHostWrapper {
- calcCommonPreUserArgs(builder)
- }
+ calcCommonPreUserArgs(builder)
processGccFlags(builder)
+
+ remoteBuildUsed := false
if !builder.cfg.isHostWrapper {
- allowCCache := true
- if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
- return nil, err
+ var err error
+ if remoteBuildUsed, err = processRemoteBuildAndCCacheFlags(!enableRusage, builder); err != nil {
+ return remoteBuildUsed, nil, err
}
}
- return builder.build(), nil
+ return remoteBuildUsed, builder.build(), nil
}
func calcCommonPreUserArgs(builder *commandBuilder) {
builder.addPreUserArgs(builder.cfg.commonFlags...)
if !builder.cfg.isHostWrapper {
+ processLibGCCFlags(builder)
processPieFlags(builder)
processThumbCodeFlags(builder)
processStackProtectorFlags(builder)
@@ -231,18 +362,18 @@ func calcCommonPreUserArgs(builder *commandBuilder) {
processSanitizerFlags(builder)
}
-func processGomaCCacheFlags(allowCCache bool, builder *commandBuilder) (err error) {
- gomaccUsed := false
+func processRemoteBuildAndCCacheFlags(allowCCache bool, builder *commandBuilder) (remoteBuildUsed bool, err error) {
+ remoteBuildUsed = false
if !builder.cfg.isHostWrapper {
- gomaccUsed, err = processGomaCccFlags(builder)
+ remoteBuildUsed, err = processRemoteBuildFlags(builder)
if err != nil {
- return err
+ return remoteBuildUsed, err
}
}
- if !gomaccUsed && allowCCache {
+ if !remoteBuildUsed && allowCCache {
processCCacheFlag(builder)
}
- return nil
+ return remoteBuildUsed, nil
}
func getAbsWrapperPath(env env, wrapperCmd *command) (string, error) {