Native Coverage support in Soong (gcov)

This is configured the same as make -- a global NATIVE_COVERAGE=true
flag to allow native coverage, then COVERAGE_PATHS=path1,path2,... to
turn it on for certain paths.

There are .gcnodir files exported to Make and saved in $OUT/coverage/...
files which are `ar` archives containing all of the compiler-produced
.gcno files for a particular executable / shared library.

Unlike the Make implementation, this only passes links the helper
library (automatically through --coverage) when one of the object files
or static libraries being used actually has coverage enabled.

Host support is currently disabled, since we set -nodefaultlibs, which
prevents libclang_rt.profile-*.a from being picked up automatically.

Bug: 32749731
Test: NATIVE_COVERAGE=true COVERAGE_PATHS=system/core/libcutils m -j libbacktrace libutils tombstoned
      $OUT/coverage/system/lib*/libcutils.gcnodir looks correct (self)
      $OUT/coverage/system/lib*/libbacktrace.gcnodir looks correct (static)
      $OUT/coverage/system/lib*/libutils.gcnodir doesn't exist (shared)
      $OUT/coverage/system/bin/tombstoned.gcnodir looks correct (executable)
Test: NATIVE_COVERAGE=true COVERAGE_PATHS=external/libcxxabi m -j libc++
      Confirm that $OUT/coverage/system/lib*/libc++.gcnodir looks correct (whole_static_libs)
Change-Id: I48aaa0ba8d76e50e9c2d1151421c0c6dc8ed79a9
diff --git a/cc/builder.go b/cc/builder.go
index c9a6722..9a871d5 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -198,6 +198,7 @@
 	toolchain   config.Toolchain
 	clang       bool
 	tidy        bool
+	coverage    bool
 
 	groupStaticLibs bool
 
@@ -207,21 +208,24 @@
 }
 
 type Objects struct {
-	objFiles  android.Paths
-	tidyFiles android.Paths
+	objFiles      android.Paths
+	tidyFiles     android.Paths
+	coverageFiles android.Paths
 }
 
 func (a Objects) Copy() Objects {
 	return Objects{
-		objFiles:  append(android.Paths{}, a.objFiles...),
-		tidyFiles: append(android.Paths{}, a.tidyFiles...),
+		objFiles:      append(android.Paths{}, a.objFiles...),
+		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
+		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 	}
 }
 
 func (a Objects) Append(b Objects) Objects {
 	return Objects{
-		objFiles:  append(a.objFiles, b.objFiles...),
-		tidyFiles: append(a.tidyFiles, b.tidyFiles...),
+		objFiles:      append(a.objFiles, b.objFiles...),
+		tidyFiles:     append(a.tidyFiles, b.tidyFiles...),
+		coverageFiles: append(a.coverageFiles, b.coverageFiles...),
 	}
 }
 
@@ -234,6 +238,10 @@
 	if flags.tidy && flags.clang {
 		tidyFiles = make(android.Paths, 0, len(srcFiles))
 	}
+	var coverageFiles android.Paths
+	if flags.coverage {
+		coverageFiles = make(android.Paths, 0, len(srcFiles))
+	}
 
 	cflags := flags.globalFlags + " " + flags.cFlags + " " + flags.conlyFlags
 	cppflags := flags.globalFlags + " " + flags.cFlags + " " + flags.cppFlags
@@ -268,12 +276,14 @@
 		var moduleCflags string
 		var ccCmd string
 		tidy := flags.tidy && flags.clang
+		coverage := flags.coverage
 
 		switch srcFile.Ext() {
 		case ".S", ".s":
 			ccCmd = "gcc"
 			moduleCflags = asflags
 			tidy = false
+			coverage = false
 		case ".c":
 			ccCmd = "gcc"
 			moduleCflags = cflags
@@ -300,11 +310,19 @@
 			ccCmd = gccCmd(flags.toolchain, ccCmd)
 		}
 
+		var implicitOutputs android.WritablePaths
+		if coverage {
+			gcnoFile := android.ObjPathWithExt(ctx, subdir, srcFile, "gcno")
+			implicitOutputs = append(implicitOutputs, gcnoFile)
+			coverageFiles = append(coverageFiles, gcnoFile)
+		}
+
 		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
-			Rule:      cc,
-			Output:    objFile,
-			Input:     srcFile,
-			OrderOnly: deps,
+			Rule:            cc,
+			Output:          objFile,
+			ImplicitOutputs: implicitOutputs,
+			Input:           srcFile,
+			OrderOnly:       deps,
 			Args: map[string]string{
 				"cFlags": moduleCflags,
 				"ccCmd":  ccCmd,
@@ -332,8 +350,9 @@
 	}
 
 	return Objects{
-		objFiles:  objFiles,
-		tidyFiles: tidyFiles,
+		objFiles:      objFiles,
+		tidyFiles:     tidyFiles,
+		coverageFiles: coverageFiles,
 	}
 }
 
@@ -341,6 +360,11 @@
 func TransformObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
+	if ctx.Darwin() {
+		transformDarwinObjToStaticLib(ctx, objFiles, flags, outputFile, deps)
+		return
+	}
+
 	arCmd := gccCmd(flags.toolchain, "ar")
 	arFlags := "crsPD"
 
@@ -360,7 +384,7 @@
 // darwin.  The darwin ar tool doesn't support @file for list files, and has a
 // very small command line length limit, so we have to split the ar into multiple
 // steps, each appending to the previous one.
-func TransformDarwinObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
+func transformDarwinObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
 	flags builderFlags, outputPath android.ModuleOutPath, deps android.Paths) {
 
 	arFlags := "cqs"
@@ -599,6 +623,20 @@
 	})
 }
 
+func TransformCoverageFilesToLib(ctx android.ModuleContext,
+	inputs Objects, flags builderFlags, baseName string) android.OptionalPath {
+
+	if len(inputs.coverageFiles) > 0 {
+		outputFile := android.PathForModuleOut(ctx, baseName+".gcnodir")
+
+		TransformObjToStaticLib(ctx, inputs.coverageFiles, flags, outputFile, nil)
+
+		return android.OptionalPathForPath(outputFile)
+	}
+
+	return android.OptionalPath{}
+}
+
 func CopyGccLib(ctx android.ModuleContext, libName string,
 	flags builderFlags, outputFile android.WritablePath) {