Add new symbol directory layout by build-id `/xx/xxxxxxxx.debug`. (#724)

This protocol is already supported by various tools and lldb.
e.g.: https://github.com/llvm-mirror/lldb/blob/d01083a850f577b85501a0902b52fd0930de72c7/source/Symbol/LocateSymbolFile.cpp#L311

Among others it is used by Fuchisa OS builds.
diff --git a/internal/driver/cli.go b/internal/driver/cli.go
index 237cc33..a9cae92 100644
--- a/internal/driver/cli.go
+++ b/internal/driver/cli.go
@@ -363,5 +363,6 @@
 	"   PPROF_TOOLS        Search path for object-level tools\n" +
 	"   PPROF_BINARY_PATH  Search path for local binary files\n" +
 	"                      default: $HOME/pprof/binaries\n" +
-	"                      searches $name, $path, $buildid/$name, $path/$buildid\n" +
+	"                      searches $buildid/$name, $buildid/*, $path/$buildid,\n" +
+	"                      ${buildid:0:2}/${buildid:2}.debug, $name, $path\n" +
 	"   * On Windows, %USERPROFILE% is used instead of $HOME"
diff --git a/internal/driver/fetch.go b/internal/driver/fetch.go
index 26fee83..9865583 100644
--- a/internal/driver/fetch.go
+++ b/internal/driver/fetch.go
@@ -409,6 +409,10 @@
 					fileNames = append(fileNames, matches...)
 				}
 				fileNames = append(fileNames, filepath.Join(path, m.File, m.BuildID)) // perf path format
+				// Llvm buildid protocol: the first two characters of the build id
+				// are used as directory, and the remaining part is in the filename.
+				// e.g. `/ab/cdef0123456.debug`
+				fileNames = append(fileNames, filepath.Join(path, m.BuildID[:2], m.BuildID[2:]+".debug"))
 			}
 			if m.File != "" {
 				// Try both the basename and the full path, to support the same directory
diff --git a/internal/driver/fetch_test.go b/internal/driver/fetch_test.go
index 289dc24..bbdb14e 100644
--- a/internal/driver/fetch_test.go
+++ b/internal/driver/fetch_test.go
@@ -59,6 +59,9 @@
 	os.MkdirAll(filepath.Join(tempdir, "pprof", "binaries", "abcde10001"), 0700)
 	os.Create(filepath.Join(tempdir, "pprof", "binaries", "abcde10001", "binary"))
 
+	os.MkdirAll(filepath.Join(tempdir, "pprof", "binaries", "fg"), 0700)
+	os.Create(filepath.Join(tempdir, "pprof", "binaries", "fg", "hij10001.debug"))
+
 	obj := testObj{tempdir}
 	os.Setenv(homeEnv(), tempdir)
 	for _, tc := range []struct {
@@ -71,6 +74,7 @@
 		{"", "/prod/path/binary", "abcde10001", filepath.Join(tempdir, "pprof/binaries/abcde10001/binary"), 0},
 		{"/alternate/architecture", "/usr/bin/binary", "", "/alternate/architecture/binary", 0},
 		{"/alternate/architecture", "/usr/bin/binary", "abcde10001", "/alternate/architecture/binary", 0},
+		{"", "", "fghij10001", filepath.Join(tempdir, "pprof/binaries/fg/hij10001.debug"), 0},
 		{"/nowhere:/alternate/architecture", "/usr/bin/binary", "fedcb10000", "/usr/bin/binary", 1},
 		{"/nowhere:/alternate/architecture", "/usr/bin/binary", "abcde10002", "/usr/bin/binary", 1},
 	} {
@@ -154,6 +158,8 @@
 		return testFile{file, "fedcb10000"}, nil
 	case filepath.Join(o.home, "pprof/binaries/abcde10001/binary"):
 		return testFile{file, "abcde10001"}, nil
+	case filepath.Join(o.home, "pprof/binaries/fg/hij10001.debug"):
+		return testFile{file, "fghij10001"}, nil
 	}
 	return nil, fmt.Errorf("not found: %s", file)
 }