Enable gapit trace with local application

Example:
gapit trace -local-app ./bloom -out trace.gfxtrace

Test: Manually tested with vulkan sample applications
Change-Id: I89953120dddd2d27bd00d1d86754c8c851403285
diff --git a/cmd/gapit/trace.go b/cmd/gapit/trace.go
index 4c836b4..04860c2 100644
--- a/cmd/gapit/trace.go
+++ b/cmd/gapit/trace.go
@@ -21,6 +21,7 @@
 	"os"
 	"path/filepath"
 	"regexp"
+	"runtime"
 	"time"
 
 	"android.googlesource.com/platform/tools/gpu/client/adb"
@@ -28,11 +29,13 @@
 	"android.googlesource.com/platform/tools/gpu/client/android/apk"
 	"android.googlesource.com/platform/tools/gpu/client/bind"
 	"android.googlesource.com/platform/tools/gpu/client/gapii"
+	"android.googlesource.com/platform/tools/gpu/client/process"
 	"android.googlesource.com/platform/tools/gpu/framework/app"
 	"android.googlesource.com/platform/tools/gpu/framework/device"
 	"android.googlesource.com/platform/tools/gpu/framework/file"
 	"android.googlesource.com/platform/tools/gpu/framework/log"
 	"android.googlesource.com/platform/tools/gpu/framework/task"
+	"android.googlesource.com/platform/tools/gpu/gapid/pkgdata"
 )
 
 var (
@@ -40,6 +43,7 @@
 	duration        time.Duration
 	output          string
 	localPort       int
+	localApp        file.Path
 	observeFrames   uint
 	observeDraws    uint
 	disablePCS      bool
@@ -65,6 +69,7 @@
 	verb.Flags.DurationVar(&duration, "d", 0, "duration to trace for")
 	verb.Flags.StringVar(&output, "out", "", "the file to generate")
 	verb.Flags.IntVar(&localPort, "local", 0, "capture a local program instead of using ADB")
+	verb.Flags.Var(&localApp, "local-app", "a local program to trace")
 	verb.Flags.UintVar(&observeFrames, "observe-frames", 0, "capture the framebuffer every n frames (0 to disable)")
 	verb.Flags.UintVar(&observeDraws, "observe-draws", 0, "capture the framebuffer every n draws (0 to disable)")
 	verb.Flags.BoolVar(&disablePCS, "disable-pcs", false, "disable pre-compiled shaders")
@@ -98,6 +103,49 @@
 		options.Flags |= gapii.RecordErrorState
 	}
 
+	if !localApp.IsEmpty() {
+		// Run the local application with VK_LAYER_PATH, VK_INSTANCE_LAYERS,
+		// VK_DEVICE_LAYERS and LD_PRELOAD set to correct values to load the spy
+		// layer.
+		localAbi := device.UnknownABI
+		switch runtime.GOOS {
+		case "linux":
+			localAbi = device.LinuxX86_64
+		default:
+			return fmt.Errorf("Unsupported OS for local tracing")
+		}
+
+		var err error
+		if vulkanLayerPath.IsEmpty() {
+			vulkanLayerPath, err = pkgdata.File(ctx, localAbi, "LibVkLayerGraphicsSpy.so")
+		}
+		if err != nil {
+			return err
+		}
+		if gapiiPath.IsEmpty() {
+			// TODO (qining): library name may change for different OS/ABI
+			gapiiPath, err = pkgdata.File(ctx, localAbi, "libgapii.so")
+		}
+		if err != nil {
+			return err
+		}
+		libGapiiDirStr := filepath.Dir(gapiiPath.String())
+		libGapiiPathStr := filepath.Join(libGapiiDirStr, "libgapii.so")
+		vulkanLayerDirStr := filepath.Dir(vulkanLayerPath.String())
+
+		os.Setenv("VK_LAYER_PATH", vulkanLayerDirStr)
+		os.Setenv("VK_INSTANCE_LAYERS", "VkGraphicsSpy")
+		os.Setenv("VK_DEVICE_LAYERS", "VkGraphicsSpy")
+		os.Setenv("LD_PRELOAD", libGapiiPathStr)
+		boundPort, err := process.Start(ctx, localApp.String())
+		if err != nil {
+			return err
+		}
+		if localPort == 0 {
+			localPort = boundPort
+		}
+	}
+
 	if localPort != 0 {
 		return captureLocal(ctx, flags, localPort, options)
 	}
diff --git a/gapid/pkgdata/layout.go b/gapid/pkgdata/layout.go
index c40dd77..1796d49 100644
--- a/gapid/pkgdata/layout.go
+++ b/gapid/pkgdata/layout.go
@@ -35,11 +35,11 @@
 	device.Android:   "android",
 }
 
-var pkgABIToDir = map[string]string{
-	"armeabi":     "armeabi-v7a",
-	"armeabi-v7a": "armeabi-v7a",
-	"arm64-v8a":   "arm64-v8a",
-	"x86":         "x86",
+var pkgABIToDir = map[device.Architecture]string{
+	device.ARMv7a: "armeabi-v7a",
+	device.ARMv8a: "arm64-v8a",
+	device.X86:    "x86",
+	device.X86_64: "x86_64",
 }
 
 // pkgLayout is the file layout used when running executables from a packaged
@@ -49,7 +49,7 @@
 }
 
 func (l pkgLayout) File(ctx log.Context, abi device.ABI, name string) (file.Path, error) {
-	return l.root.Join(packageOSToDir[abi.OS], pkgABIToDir[abi.Name], name), nil
+	return l.root.Join(packageOSToDir[abi.OS], pkgABIToDir[abi.Architecture], name), nil
 }
 
 func (l pkgLayout) Strings(ctx log.Context) (file.Path, error) {