[cmd] Delete dump_fuchsia_symbols, which has moved to //tools

New location/name:
https://fuchsia.googlesource.com/tools/+/master/cmd/dump_breakpad_symbols

INTK-388

Change-Id: I1c545796f5c79e47390a884d813ce91faef763f6
diff --git a/cmd/dump_fuchsia_symbols/main.go b/cmd/dump_fuchsia_symbols/main.go
deleted file mode 100755
index 668a6ca..0000000
--- a/cmd/dump_fuchsia_symbols/main.go
+++ /dev/null
@@ -1,320 +0,0 @@
-///bin/true ; exec /usr/bin/env go run "$0" "$@"
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package main
-
-import (
-	// TODO(kjharland): Use a safer hash algorithm. sha256 or sha2, etc.
-	"crypto/sha1"
-	"encoding/hex"
-	"encoding/json"
-	"errors"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"log"
-	"os"
-	"os/exec"
-	"path"
-	"strings"
-)
-
-const usage = `usage: dump_fuchsia_symbols [options] file1 file2 ... fileN
-
-Dumps symbol data from a collection of IDs files. IDs files are generated as
-part of the build and contain a number of newline-separate records which have
-the syntax:
-
-  <hash-value> <absolute-path>
-
-This command does not care about <hash-value>.  <absolute-path> is the path to a
-binary generated as part of the Fuchsia build. This command collects every
-<absolute-path> from each of file1, file2 ... fileN and dumps symbol data for
-the binaries at each of those paths.  Duplicate paths are skipped.
-
-The output is a collection of symbol files, one for each binary, using an
-arbitrary naming scheme to ensure that every output file name is unique.
-
-Example invocation:
-
-$ dump_fuchsia_symbols \
-	-out-dir=/path/to/output/ \
-	-dump-syms-path=/path/to/breakpad/dump_syms \
-	-summary-file=/path/to/summary \
-	/path/to/ids1.txt
-`
-
-// Options represents the command line options.
-type Options struct {
-	outdir       string
-	dryRun       bool
-	dumpSymsPath string
-	summaryFile  string
-}
-
-func main() {
-	RunMain(os.Args)
-}
-
-// RunMain implements the main() function. Visible for testing.
-func RunMain(args []string) {
-	f, options, err := ParseFlags(args)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	if processIdsFiles(f.Args(), options) {
-		log.Println("finished with errors")
-		os.Exit(1)
-	}
-}
-
-// ParseFlags parses command line parameters. Visible for testing.
-func ParseFlags(args []string) (*flag.FlagSet, *Options, error) {
-	f := flag.NewFlagSet(args[0], flag.ContinueOnError)
-	f.Usage = func() {
-		fmt.Println(usage)
-		flag.PrintDefaults()
-		os.Exit(0)
-	}
-
-	var options Options
-	// First set the flags ...
-	f.StringVar(&options.summaryFile, "summary-file", "",
-		"Path to a JSON file to write that maps each binary to its symbol file. "+
-			"The output looks like {'/path/to/binary': '$out-dir/path/to/file'}. "+
-			"Prints to stdout by default.",
-	)
-	f.StringVar(&options.outdir, "out-dir", "",
-		"The directory where symbol output should be written")
-	f.StringVar(&options.dumpSymsPath, "dump-syms-path", "",
-		"Path to the breakpad tools `dump_syms` executable")
-	f.BoolVar(&options.dryRun, "dry-run", false,
-		"Print the dump_syms commands to run, without running them, then exit. "+
-			"summary-file is always written to stdout during a dry-run.",
-	)
-	f.Parse(args[1:])
-
-	// Ensure at least one file was given.
-	if f.NArg() < 1 {
-		return nil, nil, errors.New("at least one ids.txt file is required")
-	}
-	// Ensure path to dump_syms is specified
-	if options.dumpSymsPath == "" {
-		return nil, nil, errors.New("-dump-syms-path is required")
-	}
-	// Ensure output directory was given.
-	if options.outdir == "" {
-		return nil, nil, errors.New("-out-dir is required")
-	}
-
-	return f, &options, nil
-}
-
-// processidsFiles dumps symbol data for each executable in a set of ids files.
-//
-// Returns true iff any errors occurred.
-func processIdsFiles(idsFiles []string, options *Options) (gotErrors bool) {
-	// Indicates whether we've seen a binary path already.  Duplicate paths are
-	// skipped.
-	visited := make(map[string]bool)
-	binaryToSymbolFile := make(map[string]string)
-
-	// Confirm that the user is performing a dry-run.
-	if options.dryRun {
-		log.Println("Performing dry-run")
-	}
-
-	// Iterate through the given set of filepaths.
-	for _, idsFile := range idsFiles {
-		// Extract the paths to each binary from the IDs file.
-		binaryPaths, err := extractBinaryPaths(idsFile)
-		if err != nil {
-			logError("failed to extract paths from %s: %v", idsFile, err)
-			gotErrors = true
-			continue
-		}
-
-		// Generate symbol data for each binary.
-		for _, binaryPath := range binaryPaths {
-			// Check whether we've seen this path already. Skip if so.
-			if _, ok := visited[binaryPath]; ok {
-				continue
-			}
-			// Record that we've seen this binary path.
-			visited[binaryPath] = true
-
-			// Generate the symbol file path.
-			symbolFile := path.Join(options.outdir, hashText(binaryPath)+".sym")
-
-			// Record the mapping in the summary.
-			binaryToSymbolFile[binaryPath] = symbolFile
-
-			// Log what we're about to do. If this a dry run, say so and
-			// continue without dumping symbols.
-			info := fmt.Sprintf("dumping symbols for %s into %s", binaryPath, symbolFile)
-			if options.dryRun {
-				log.Println("DRY_RUN: " + info)
-				continue
-			}
-			log.Println(info)
-
-			// Dump the symbol data to disk. Record an error
-			if err := dumpSymbolData(binaryPath, symbolFile, options.dumpSymsPath); err != nil {
-				logError("%v", err)
-				gotErrors = true
-				continue
-			}
-		}
-	}
-
-	var summaryFile *os.File
-
-	// If no summary file path was given, write the summmary to stdout.
-	if options.summaryFile == "" || options.dryRun {
-		summaryFile = os.Stdout
-	} else {
-		var err error
-		summaryFile, err = os.Create(options.summaryFile)
-		if err != nil {
-			logError("failed to open summary file %s: %v", options.summaryFile, err)
-			gotErrors = true
-			return
-		}
-	}
-
-	if err := writeSummary(binaryToSymbolFile, summaryFile); err != nil {
-		logError("failed to output summary %s: %v", options.summaryFile, err)
-		gotErrors = true
-		return
-	}
-
-	return
-}
-
-// Returns a sha1 hash of the input text.
-func hashText(text string) string {
-	hash := sha1.New()
-	n, err := hash.Write([]byte(text))
-	if err != nil {
-		panic(err)
-	}
-	if n == 0 {
-		// Empty text should never be passed to this function and likely signifies
-		// an error in the input file. Panic here as well.
-		panic("0 bytes written for hash of input text '" + text + "'")
-	}
-
-	return hex.EncodeToString(hash.Sum(nil))
-}
-
-// Logs an error message.
-//
-// format and args work the same as with fmt.Printf.
-func logError(format string, args ...interface{}) {
-	log.Printf("ERROR: "+format, args...)
-}
-
-// Writes the summary.
-func writeSummary(summary map[string]string, file *os.File) error {
-	// TODO(kjharland): Sort the keys before priting to ensure predictable
-	// output or use a different data structure with a consistent ordering.
-
-	// Serialize the summary.
-	summaryBytes, err := json.MarshalIndent(summary, "", "  ")
-	if err != nil {
-		return fmt.Errorf("json marhsal failed: %v", err)
-	}
-
-	// Write the summary.
-	if _, err := file.Write(summaryBytes); err != nil {
-		return fmt.Errorf("write failed: %v", err)
-	}
-
-	return nil
-}
-
-// Extracts a list of absolute paths to binaries from an idsFile.
-//
-// See the helptext for this command for info about the idsFile.  This function
-// handles malformed input gracefully, logging errors rather than returning
-// early.
-//
-// Returns the list of binary paths.
-//
-// TODO(kjharland): Use https://fuchsia.googlesource.com/tools/+/master/symbolize/repo.go
-// and delete this.
-func extractBinaryPaths(idsFile string) ([]string, error) {
-	var binaryPaths []string
-
-	// Read file contents.
-	idsFileBytes, err := ioutil.ReadFile(idsFile)
-	if err != nil {
-		return nil, err
-	}
-
-	idsFileLines := strings.Split(string(idsFileBytes), "\n")
-
-	// Extract the path to the binary from each line.
-	for _, line := range idsFileLines {
-		line = strings.TrimSpace(line)
-		if len(line) == 0 {
-			// Skip empty lines gracefully.
-			continue
-		}
-
-		fields := strings.Split(line, " ")
-		if len(fields) != 2 {
-			// Lines should only have two columns. Abort if input is malformed.
-			return nil, fmt.Errorf("malformed line in %s: %s", idsFile, line)
-		}
-
-		binaryPaths = append(binaryPaths, fields[1])
-	}
-
-	return binaryPaths, nil
-}
-
-// Runs the breakpad tool `dump_syms` on the binary at the given absolute path,
-// Then writes the symbol data to the given path.
-func dumpSymbolData(binaryPath, symbolFile, dumpSymsPath string) error {
-	// Run the dump_syms command.
-	symbolData, err := exec.Command(dumpSymsPath, binaryPath).Output()
-	if err != nil {
-		return fmt.Errorf("failed to execute %s: %s", dumpSymsPath, err)
-	}
-
-	// Many Fuchsia binaries are built as "something.elf", but then packaged as
-	// just "something". In the ids.txt file, the name still includes the ".elf"
-	// extension, which dump_syms emits into the .sym file, and the crash server
-	// uses as part of the lookup.  The binary name and this value written to
-	// the .sym file must match, so if the first header line ends in ".elf"
-	// strip it off.  This line usually looks something like:
-	// MODULE Linux x86_64 094B63014248508BA0636AD3AC3E81D10 sysconf.elf
-	lines := strings.SplitN(string(symbolData), "\n", 2)
-	if len(lines) != 2 {
-		return fmt.Errorf("got <2 lines in symbol data for %s", binaryPath)
-	}
-
-	// Make sure the first line is not empty.
-	lines[0] = strings.TrimSpace(lines[0])
-	if lines[0] == "" {
-		return fmt.Errorf("unexpected blank first line in symbol data for %s", binaryPath)
-	}
-
-	// Strip .elf from header if it exists.
-	if strings.HasSuffix(lines[0], ".elf") {
-		lines[0] = strings.TrimSuffix(lines[0], ".elf")
-		// Join the new lines of the symbol data.
-		symbolData = []byte(strings.Join(lines, "\n"))
-	}
-
-	// Write the symbol file.
-	if err := ioutil.WriteFile(symbolFile, []byte(symbolData), 0644); err != nil {
-		return fmt.Errorf("could not write output file %s: %v", symbolFile, err)
-	}
-
-	return nil
-}
diff --git a/cmd/dump_fuchsia_symbols/main_test.go b/cmd/dump_fuchsia_symbols/main_test.go
deleted file mode 100644
index 7b09549..0000000
--- a/cmd/dump_fuchsia_symbols/main_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package main_test
-
-import (
-	"io/ioutil"
-	"path/filepath"
-	"testing"
-
-	cmd "fuchsia.googlesource.com/infra/infra/cmd/dump_fuchsia_symbols"
-)
-
-func TestParseFlags(t *testing.T) {
-	// Parses flags from the given command line args.
-	parseFlags := func(args []string) error {
-		// Prepend the command name to args to avoid having to repeat it
-		// in every test case
-		args = append([]string{"dump_fuchsia_symbols"}, args...)
-		_, _, err := cmd.ParseFlags(args)
-		return err
-	}
-
-	// Expects command line parsing to succeed. Fails otherwise.
-	expectSuccess := func(t *testing.T, args []string) {
-		if err := parseFlags(args); err != nil {
-			t.Errorf("Failed to parse command line: %+v, %v", args, err)
-		}
-	}
-
-	// Expects command line parsing to fail. Fails otherwise.
-	expectFailure := func(t *testing.T, args []string) {
-		if parseFlags(args) == nil {
-			t.Errorf("Expected error when parsing %+v. Got nil", args)
-		}
-	}
-
-	t.Run("should succeed if valid arguments are given", func(t *testing.T) {
-		t.Run("when there are multiple input files", func(t *testing.T) {
-			expectSuccess(t, []string{
-				"-out-dir=output_directory",
-				"-dump-syms-path=/path/to/dump_syms",
-				"ids.txt",
-				"ids2.txt",
-			})
-		})
-
-		t.Run("when -dry-run=true", func(t *testing.T) {
-			// explicit "-dry-run=true"
-			expectSuccess(t, []string{
-				"-dry-run=true",
-				"-out-dir=output_directory",
-				"-dump-syms-path=/path/to/dump_syms",
-				"ids.txt",
-			})
-			// Just "-dry-run"
-			expectSuccess(t, []string{
-				"-dry-run",
-				"-out-dir=output_directory",
-				"-dump-syms-path=/path/to/dump_syms",
-				"ids.txt",
-			})
-		})
-
-	})
-
-	t.Run("should fail if -out-dir is missing", func(t *testing.T) {
-		expectFailure(t, []string{
-			"-dry-run",
-			"-dump-syms-path=/path/to/dump_syms",
-			"ids.txt",
-		})
-	})
-
-	t.Run("should fail if -dump-syms-path is missing", func(t *testing.T) {
-		expectFailure(t, []string{
-			"-out-dir=output_directory",
-			"-dry-run",
-			"ids.txt",
-		})
-	})
-	t.Run("should fail if input file is missing", func(t *testing.T) {
-		expectFailure(t, []string{
-			"-out-dir=output_directory",
-			"-dry-run",
-			"-dump-syms-path=/path/to/dump_syms",
-		})
-	})
-}
-
-// Examples-as-tests below this line.  "Output:" at the end of each function
-// verifies that what is printed on stdout matches the rest of the comment.
-// If there is a difference, `go test ./...` will fail.
-
-func ExampleWithOneInputFile() {
-	// Create a testing input file with fake hash values and binary paths.
-	inputFile := createTempFile("ids.txt", `
-01634b09 /path/to/binaryA.elf
-02298167 /path/to/binaryB
-025abbbc /path/to/binaryC.so
-`)
-
-	// Run the command in dry-run mode so that we can verify the list of
-	// commands that would actually run, given the test input file.
-	cmd.RunMain([]string{
-		"dump_fuchsia_symbols",
-		"-dry-run",
-		"-out-dir=/out",
-		"-dump-syms-path=/path/to/dump_syms",
-		inputFile,
-	})
-
-	// Output:
-	//{
-	//   "/path/to/binaryA.elf": "/out/fe9881defb9ed1ddb9a89c38be973515f6ad7f0f.sym",
-	//   "/path/to/binaryB": "/out/f03de72df78157dd14ae1cc031ddba9873947179.sym",
-	//   "/path/to/binaryC.so": "/out/edbe4e45241c98dcde3538160073a0d6b097b780.sym"
-	//}
-}
-
-func ExampleWithMultipleInputFiles() {
-	// Create a testing input file with bogus hash values and binary paths.
-	inputFileA := createTempFile("idsA.txt", `
-01634b09 /path/to/binaryA.elf
-02298167 /path/to/binaryB
-025abbbc /path/to/binaryC.so
-`)
-
-	// Create a testing input file with bogus hash values and binary paths.
-	inputFileB := createTempFile("idsB.txt", `
-01634b09 /path/to/binaryD
-02298167 /path/to/binaryE
-025abbbc /path/to/binaryF
-`)
-
-	// Run the command in dry-run mode so that we can verify the list of
-	// commands that would actually run, given the test input file.
-	cmd.RunMain([]string{
-		"dump_fuchsia_symbols",
-		"-dry-run",
-		"-out-dir=/out",
-		"-dump-syms-path=/path/to/dump_syms",
-		inputFileA,
-		inputFileB,
-	})
-
-	// Output:
-	//{
-	//   "/path/to/binaryA.elf": "/out/fe9881defb9ed1ddb9a89c38be973515f6ad7f0f.sym",
-	//   "/path/to/binaryB": "/out/f03de72df78157dd14ae1cc031ddba9873947179.sym",
-	//   "/path/to/binaryC.so": "/out/edbe4e45241c98dcde3538160073a0d6b097b780.sym",
-	//   "/path/to/binaryD": "/out/8541277ee6941ac4c3c9ab2fc68edfb4c420861e.sym",
-	//   "/path/to/binaryE": "/out/906bc6368e6462a6cf7b78328a675ce57ef82209.sym",
-	//   "/path/to/binaryF": "/out/302cb9c3745652180c25e5da2ca3e420b8dd4e25.sym"
-	//}
-}
-
-func ExampleSkipDuplicatePaths() {
-	// Create a testing input file with bogus hash values and binary paths.
-	inputFile := createTempFile("ids.txt", `
-01634b09 /path/to/binaryA
-02298167 /path/to/binaryA
-`)
-
-	// Run the command in dry-run mode so that we can verify the list of
-	// commands that would actually run, given the test input file.
-	cmd.RunMain([]string{
-		"dump_fuchsia_symbols",
-		"-dry-run",
-		"-out-dir=/out",
-		"-dump-syms-path=/path/to/dump_syms",
-		inputFile,
-	})
-
-	// Output:
-	// {
-	//   "/path/to/binaryA": "/out/43e5a3c9eb9829f2eb11007223de1fb0b721a909.sym"
-	// }
-}
-
-// Creates a temp file with the given name and contents for testing.
-//
-// Returns the absolute path to the file.
-//
-// TODO(kjharland): It would be nice to create an entire temp directory, and
-// create all files in that directory for testing. Then each test can
-// `defer dir.delete()` to clean up.
-func createTempFile(name, contents string) (absPath string) {
-	// Create the file.
-	file, err := ioutil.TempFile("", name)
-	if err != nil {
-		panic(err)
-	}
-
-	// Write the contents.
-	file.Write([]byte(contents))
-
-	// Grab the absolute path
-	absPath, err = filepath.Abs(file.Name())
-	if err != nil {
-		panic(err)
-	}
-	return
-}
-
-// No tests to verify the output of dump_syms. Assume it does the right thing.
-
-// TODO(kjharland): Consider creating test binaries to pass as -dump-syms-path,
-// which would let us write integration tests for this file.