compiler_wrapper: android clang-tidy warnings on used seconds

Give a warning if clang-tidy used more than TIDY_TIMEOUT/2 seconds.
Users can fix it before it gets worse and times out.

BUG=b:199451930
TEST=Use a fake Android clang-tidy.real binary,
TEST=to dump passed environment variables and run for required time.

Change-Id: I8df7e4e3bb621cbf40094c7b97494e5978d7c6a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/3200220
Reviewed-by: Chih-Hung Hsieh <chh@google.com>
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
Commit-Queue: George Burgess <gbiv@chromium.org>
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 7d949d3..986eaba 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -74,25 +74,36 @@
 	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.
-	// 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
-	}
 	warning := "%s:1:1: warning: clang-tidy aborted after %d seconds.\n"
-	fmt.Fprintf(env.stdout(), warning, sourceFile, seconds)
+	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